第一节
1.UI控件中拖线属性或方法常见错误
经典的错误
1. 错误一
描述:
reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key testLabel.'
原因: 有多余的连线
解决: 删除多余的连线
2.错误二
描述:
reason: '-[MainViewController clickBtn:]: unrecognized selector sent to instance 0x7feb69418640'
原因:找不到对应的方法
解决:1.添加对应的方法 2.删除多余的连线
2.UIView的常见属性
-
(void)addSubview:(UIView *)view;
- 添加一个子控件view
-
(void)removeFromSuperview;
- 将自己从父控件中移除
-
(UIView *)viewWithTag:(NSInteger)tag;
- 根据一个tag标识找出对应的控件(一般都是子控件)
-
@property(nonatomic) CGRect frame;
- 控件矩形框在父控件中的位置和尺寸(以父控件的左上角为坐标原点)
-
@property(nonatomic) CGRect bounds;
- 控件矩形框的位置和尺寸(以自己左上角为坐标原点,所以bounds的x、y一般为0)
-
@property(nonatomic) CGPoint center;
- 控件中点的位置(以父控件的左上角为坐标原点)
第二节
1.UIImageView的frame的设置
// 设置frame的方式
// 方式一
UIImageView *imageView = [[UIImageView alloc] init];
imageView.image = [UIImage imageNamed:@"1"];
// imageView.frame = CGRectMake(100, 100, 267, 400);
// imageView.frame = (CGRect){{100, 100},{267, 400}};
*/
// 方式二
/*
UIImageView *imageView = [[UIImageView alloc] init];
// 创建一个UIImage对象
UIImage *image = [UIImage imageNamed:@"1"];
// 设置frame
imageView.frame = CGRectMake(100, 10, image.size.width, image.size.height);
// 设置图片
imageView.image = image;
*/
// 方式三
/*
// 创建一个UIImage对象
UIImage *image = [UIImage imageNamed:@"1"];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 10, image.size.width, image.size.height)];
imageView.image = image;
*/
// 方式四
// 创建一个UIimageview对象
// 注意: initWithImage 默认就有尺寸--->图片的尺寸
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"1"]];
// 改变位置
// imageView.center = CGPointMake(200, 150);
imageView.center = CGPointMake(self.view.frame.size.width * 0.5, self.view.frame.size.height * 0.5);
2.控件的创建方式
-
一个控件有2种创建方式
- 通过代码创建
- 初始化时一定会调用initWithFrame:方法
-
通过xib\storyboard创建
- 初始化时不会调用initWithFrame:方法,只会调用initWithCoder:方法
- 初始化完毕后会调用awakeFromNib方法
注意:
有时候希望在控件初始化时做一些初始化操作,比如添加子控件、设置基本属性
这时需要根据控件的创建方式,来选择在initWithFrame:、initWithCoder:、awakeFromNib的哪个方法中操作
3.xib的加载
方法1
NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"xib文件名" owner:nil options:nil]
方法2
UINib *nib = [UINib nibWithNibName:@"xib文件名" bundle:nil];
NSArray *views = [nib instantiateWithOwner:nil options:nil];
4.加载图片的方式
- imageNamed:与
- imageWithContentsOfFile:的区别
加载Assets.xcassets这里面的图片:
1> 打包后变成Assets.car
2> 拿不到路径
3> 只能通过imageNamed:来加载图片
4> 不能通过imageWithContentsOfFile:来加载图片
放到项目中的图片:
1> 可以拿到路径
2> 能通过imageNamed:来加载图片
3> 也能通过imageWithContentsOfFile:来加载图片
图片的两种加载方式:
1> imageNamed:
a. 就算指向它的指针被销毁,该资源也不会被从内存中干掉
b. 放到Assets.xcassets的图片,默认就有缓存
c. 使用场景:图片经常被使用
2> imageWithContentsOfFile:
a. 指向它的指针被销毁,该资源会被从内存中干掉
b. 放到项目中的图片就没有缓存
c. 使用场景:不经常用,大批量的图片
5.Xcode插件大全
http://www.cocoachina.com/industry/20130918/7022.html
-
必备
- 文档注释生成:https://github.com/onevcat/VVDocumenter-Xcode
- 自动检索图片名:https://github.com/ksuther/KSImageNamed-Xcode
- 插件管理工具:https://github.com/mneorr/Alcatraz
-
移除插件(可以使用上面提到的插件管理工具Alcatraz)
- 到~/Library/Application Support/Developer/Shared/Xcode/Plug-ins文件夹中删除
插件失效修复:http://joeshang.github.io/2015/04/10/fix-xcode-upgrade-plugin-invalid/
第三节
1.九宫格
处在同一列的x值是一样的
处在同一行的y值是一样的
在九宫格中计算每一个小格子的位置
x值 = (小格子的宽 + 小格子之间的水平间距)*(格子的下标%格子的总列数)
y值 = (小格子的高 + 小格子之间的垂直间距)*(格子的下标/格子的总列数)
第四节
1.懒加载
1.作用:
1>用到的时候再加载
2>全局只会被加载一次
3>全局都可以使用
过程:
1.重写成员变量的get方法
2.在get方法中判断:
1>如果为空,加载数据
2>如果不为空,就直接返回数据
第五节
1.自定义控件与模型
- 加载自定义控件分为两种:一种是代码加载自定义控件
* 首先在自定义控件的.m文件中重写initwithframe方法,在这个方法中创建控件
* 然后再laysubview方法中给控件设置尺寸
* 在.h文件中设置一个模型属性,然后在.m文件中充血模型属性的set方法,把传递进来的模型属性的值设置给对应控件
为自定义控件充血init 和 类工厂方法,
-
宁外一种加载自定义控件的方法的事通过xib加载
首先创建xib的文件,布局xib,关联xib对应的class
在xib对应的class类的.h文件中,设置模型属性
在.m文件中充血模型的set方法
在.m属性中拉线给xib控件赋值
2.自定义按钮
注意: 在按钮外面改的尺寸,按钮的内部都会覆盖掉
/*
button.titleLabel.frame = CGRectMake(0, 0, 100, 70);
button.imageView.frame = CGRectMake(100, 0, 70, 70);
*/
正确的做法
[button titleRectForContentRect:CGRectMake(0, 0, 100, 70)];
[button imageRectForContentRect:CGRectMake(100, 0, 70, 70)];
如果需要改变按钮控件内部的图片和label的frame的话又两种方法
一个是重写按钮内部的layoutsubview方法
另外一个是重写按钮内部的
(CGRect)titleRectForContentRect:(CGRect)contentRect
(CGRect)imageRectForContentRect:(CGRect)contentRect
3.图片的拉伸问题
UIImage *resizableImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(imageHeight * 0.5, imageWidth * 0.5, imageHeight * 0.5 -1, imageWidth * 0.5 - 1)];
另外的一个方法
// UIImage *resizableImage = [image stretchableImageWithLeftCapWidth:imageWidth * 0.5 topCapHeight:imageHeight * 0.5];
4.KVC
>1.利用KVC进行简单赋值
在简单赋值过程中会进行自动类型转换比如
[person setValue:@"19" forKeyPath:@"money"];
NSLog(@"%@-----%.2f", person.name, person.money)
打印结果整形转成浮点型
>2.KVC综合赋值
forKey和forKeyPath
1>forKeyPath 包含了所有 forKey 的功能
2>forKeyPath 进行内部的点语法,层层访问内部的属性
3>注意: key值一定要在属性中找到
>3.利用KVC修改类的私有成员变量
利用KVC修改类的私有成员变量
4.字典转模型
作用: 字典转模型
开发中是不建议使用setValuesForKeysWithDictionary:
1> 字典中的key必须在模型的属性中找到
2> 如果模型中带有模型,setValuesForKeysWithDictionary不好使
应用场景: 简单的字典转模型 ---> 框架 (MJExtention)
5.取值
6.把模型转成字典
NSDictionary *dict = [person dictionaryWithValuesForKeys:@[@"name", @"money"]];
7.取出数组中所有模型的某个属性值
XMGPerson *person1 = [[XMGPerson alloc] init];
person1.name = @"zhangsan";
person1.money = 12.99;
XMGPerson *person2 = [[XMGPerson alloc] init];
person2.name = @"zhangsi";
person2.money = 22.99;
XMGPerson *person3 = [[XMGPerson alloc] init];
person3.name = @"wangwu";
person3.money = 122.99;
NSArray *allPersons = @[person1, person2, person3];
NSArray *allPersonName = [allPersons valueForKeyPath:@"name"];
NSLog(@"%@", allPersonName);
5KVO
KVO: Key Value Observing (键值监听)--->当某个对象的属性值发生改变的时候(用KVO监听)
/*
作用:给对象绑定一个监听器(观察者)
- Observer 观察者
- KeyPath 要监听的属性
- options 选项(方法方法中拿到属性值)
*/
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
person.name = @"ls";
person.name = @"ww";
// 移除监听
[person removeObserver:self forKeyPath:@"name"];
注意增加监听的时候记得移除监听,就好像添加通知要移除通知一样
增加监听以后可以在observeValueForKeyPath这个方法中监听到属性值的改变
第六节
1.监听scrollView各种行为的3大步骤(比如让控制 器监听scrollView的行为)
- 设置scrollView的delegate(代理)为控制器对象
- 控制器要遵守UIScrollViewDelegate协议
- 控制器要实现UIScrollViewDelegate协议里面的代理方法
scrollView.delegate = 控制器;
@interface 控制器 () @end
#pragma mark - 代理 法
{
NSLog(@"scrollViewDidScroll-滚动"); }
2.代理使用的一般规律
- 作用:用来监听控件的某些行为
- 代理:是控制器对象
- 代理:是id类型,并且是弱指针(weak)
- 代理协议的格式:控件类名+Delegate,比如UIScrollViewDelegate、 UITableViewDelegate
- 代理方法:方法名一般是控件名开头,比如UIScrollView的代理方法一般以scrollView开 头
3.如何监听控件的行为
-
通过addTarget:
只有继承自UIControl的控件,才有这个功能 UIControlEventTouchUpInside : 点击事件(UIButton)
UIControlEventValueChanged : 值改变事件(UISwitch、UISegmentControl、 UISlider)
UIControlEventEditingChanged : 文字改变事件(UITextField)
通过delegate 只有拥有delegate属性的控件,才有这个功能
4.NSTimer的使用
- 开启定时器
@property (nonatomic, weak) NSTimer *timer;
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
// 返回 个 动开始执 任务的定时器
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self
// NSDefaultRunLoopMode(默认): 同一时间只能执行一个任务
// NSRunLoopCommonModes(公用): 可以分配一定的时间执行其他任务
// 作用:修改timer在runLoop中的模式为NSRunLoopCommonModes
// 目的:不管主线程在做什么操作,都会分配一定的时间处理定时器
- 关闭定时器
[self.timer invalidate];
5.UIScrollView
1.基本使用
- 介绍
- 设置内容尺寸(contentSize)
- 不能滚动的原因
2.常见属性
- 是否有弹簧效果
- 是否总是有弹簧效果
- 作用:下拉刷新,上拉加载
- 是否显示滚动条
- 滚动条的样式
- 滚动条是imageView
- 注意点:不要通过索引去subViews这个数组中去访问scrollView的子控件
3.偏移量和内边距
4.监听scrollView的各种行为
- 代理的介绍
- 代码创建scrollView和storyboard创建的区别
- 代码创建要等到真正要显示的时候才会有滚动条
如何监听scrollView已经停止滚动 有2种情况
第一种手松开,scrollView就停止滚动 第二种,用户停止拖拽,但scrollView由于惯性继续滚动,并且减速
5.内容缩放
- 通过代理告知哪一个控件需要缩放
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;
- 设置缩放的比例
* 设置minimumZoomScale
* 设置maximumZoomScale
- 监听缩放的过程
跟缩放相关的其他代理方法
即将开始缩放的时候调用
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
正在缩放的时候调用
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
- 禁止缩放的弹簧效果
第七节
1.Autolayout
-
Autolayout的2个核心概念
- 约束:通过给控件添加约束,来决定控件的位置和尺寸
- 参照:在添加约束时,是依照谁来添加(可以是父控件或者兄弟控件)
代码实现Autolayout的注意点
要先禁止autoresizing功能,设置view的下面属性为NO
view.translatesAutoresizingMaskIntoConstraints = NO;
添加约束之前,一定要保证相关控件都已经在各自的父控件上
不用再给view设置frame
一个NSLayoutConstraint对象就代表一个约束
创建约束对象的常用方法
+(id)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
view1 :要约束的控件
attr1 :约束的类型(做怎样的约束)
relation :与参照控件之间的关系
view2 :参照的控件
attr2 :约束的类型(做怎样的约束)
multiplier :乘数
c :常量
[图片上传失败...(image-cbf8ee-1520169654402)]
在添加时要注意目标view需要遵循以下规则:
- 1)对于两个同层级view之间的约束关系,添加到它们的父view上
[图片上传失败...(image-42483c-1520169654402)]
- 2)对于两个不同层级view之间的约束关系,添加到他们最近的共同父view上
[图片上传失败...(image-dea5d5-1520169654402)]
- 3)对于有层次关系的两个view之间的约束关系,添加到层次较高的父view上
[图片上传失败...(image-f2d463-1520169654402)]
- 在修改了约束之后,只要执行下面代码,就能做动画效果
[UIView animateWithDuration:1.0 animations:^{
[添加了约束的view的父控件 layoutIfNeeded];
}];
- 补充:
- autolayout中baselines的选项可以为label和button设置下方对齐
2.VFL语言实现autolayout
使用VFL来创建约束数组
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views;
format :VFL语句
opts :约束类型
metrics :VFL语句中用到的具体数值
views :VFL语句中用到的控件
创建一个字典(内部包含VFL语句中用到的控件)的快捷宏定义
NSDictionaryOfVariableBindings(...)
3.Masonry目前最流行的autolayout的第三方kuangjia
-
框架地址
- https://github.com/SnapKit/Masonry
Masonry两种简写的用法
默认情况下
mas_equalTo有自动包装功能,比如自动将20包装为@20
equalTo没有自动包装功能
如果添加了下面的宏,那么mas_equalTo和equalTo就没有区别
#define MAS_SHORTHAND_GLOBALS
// 注意:这个宏一定要添加到#import "Masonry.h"前面
默认情况下
width是make对象的一个属性,用来添加宽度约束用的,表示对宽度进行约束
mas_width是一个属性值,用来当做equalTo的参数,表示某个控件的宽度属性
如果添加了下面的宏,mas_width也可以写成width
#define MAS_SHORTHAND
mas_height、mas_centerX以此类推
第八节
1.���UITableView
在tableview中,如果显示的内容没有撑满整个屏幕,可以通过在尾部设置tableview。viewforfoot的属性为一个空的view来达到撑满整个屏幕的效果,也可以改变tableview的样式为group
在tableview中的cellforrowatindexpath空,���中谨记有if就要有else,因为table的cell都是循环引用的
UITableView的每一行都是一个UITableViewCell,通过dataSource的tableView:cellForRowAtIndexPath:方法来初始化每一行
UITableViewCell内部有个默认的子视图:contentView,contentView是UITableViewCell所显示内容的父视图,可显示一些辅助指示视图
注意:在开发中等号左右两边的类型要是同一类型
2.UITableViewCell
UITableViewCell重用
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.定义一个cell的标识
static NSString *ID = @”cell";
// 2.从缓存池中取出cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 3.如果缓存池中没有cell
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
注意:tableview和tableviewcell有一个共同的特点就是他们一创建出来就已经确定了样式,在接下去的代码中用访问属性的点语法也改不了他们的样式
第九节
1.在UITableview中自定义等高cell
- 第一种使用纯代码实现(frame)
新建一个继承自UITableViewCell
的子类,比如XMGTgCell
@interface XMGTgCell : UITableViewCell
@end
在XMGTgCell.m文件中
- 重写
-initWithStyle:reuseIdentifier:
方法- 在这个方法中添加所有需要显示的子控件
- 给子控件做一些初始化设置(设置字体、文字颜色等)
/**
* 在这个方法中添加所有的子控件
*/
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// ......
}
return self;
}
- 重写
-layoutSubviews
方法- 一定要调用
[super layoutSubviews]
- 在这个方法中计算和设置所有子控件的frame
- 一定要调用
/**
* 在这个方法中计算所有子控件的frame
*/
- (void)layoutSubviews
{
[super layoutSubviews];
// ......
}
在XMGTgCell.h文件中提供一个模型属性,比如XMGTg模型
@class XMGTg;
@interface XMGTgCell : UITableViewCell
/** 团购模型数据 */
@property (nonatomic, strong) XMGTg *tg;
@end
在XMGTgCell.m中重写模型属性的set方法
- 在set方法中给子控件设置模型数据
- (void)setTg:(XMGTg *)tg
{
_tg = tg;
// .......
}
在控制器中
- 注册cell的类型
[self.tableView registerClass:[XMGTgCell class] forCellReuseIdentifier:ID];
- 给cell传递模型数据
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 访问缓存池
XMGTgCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 设置数据(传递模型数据)
cell.tg = self.tgs[indexPath.row];
return cell;
}
2.在UITableview中自定义不登高的cell
给模型增加frame数据
- 所有子控件的frame
- cell的高度(cellHeight)
@interface XMGStatus : NSObject
/**** 文字\图片数据 ****/
// .....
/**** frame数据 ****/
/** 头像的frame */
@property (nonatomic, assign) CGRect iconFrame;
// .....
/** cell的高度 */
@property (nonatomic, assign) CGFloat cellHeight;
@end
- 重写模型cellHeight属性的get方法
- (CGFloat)cellHeight
{
if (_cellHeight == 0) {
// ... 计算所有子控件的frame、cell的高度
}
return _cellHeight;
}
在控制器中
- 实现一个返回cell高度的代理方法
- 在这个方法中返回indexPath位置对应cell的高度
/**
* 返回每一行cell的具体高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
XMGStatus *status = self.statuses[indexPath.row];
return status.cellHeight;
}
- 给cell传递模型数据
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 访问缓存池
XMGStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 设置数据(传递模型数据)
cell.status = self.statuses[indexPath.row];
return cell;
}
新建一个继承自UITableViewCell
的子类,比如XMGStatusCell
@interface XMGStatusCell : UITableViewCell
@end
在XMGStatusCell.m文件中
- 重写
-initWithStyle:reuseIdentifier:
方法- 在这个方法中添加所有可能需要显示的子控件
- 给子控件做一些初始化设置(设置字体、文字颜色等)
/**
* 在这个方法中添加所有可能需要显示的子控件
*/
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// ......
}
return self;
}
在XMGStatusCell.h文件中提供一个模型属性,比如XMGStatus模型
@class XMGStatus;
@interface XMGStatusCell : UITableViewCell
/** 微博模型数据 */
@property (nonatomic, strong) XMGStatus *status;
@end
在XMGStatusCell.m中重写模型属性的set方法
- 在set方法中给子控件设置模型数据
- (void)setStatus:(XMGStatus *)status
{
_status = status;
// .......
}
重写-layoutSubviews
方法
- 一定要调用
[super layoutSubviews]
- 在这个方法中设置所有子控件的frame
/**
* 在这个方法中设置所有子控件的frame
*/
- (void)layoutSubviews
{
[super layoutSubviews];
// ......
}
3自定义不等高的cell 使用storyboard
对比自定义等高cell,需要几个额外的步骤(iOS8开始才支持)
- 添加子控件和contentView之间的间距约束
- 设置tableViewCell的真实行高和估算行高
// 告诉tableView所有cell的真实高度是自动计算(根据设置的约束来计算)
self.tableView.rowHeight = UITableViewAutomaticDimension;
// 告诉tableView所有cell的估算高度
self.tableView.estimatedRowHeight = 44;
如果要支持iOS8之前
- 如果cell内部有自动换行的label,需要设置preferredMaxLayoutWidth属性
- (void)awakeFromNib
{
// 手动设置文字的最大宽度(目的是:让label知道自己文字的最大宽度,进而能够计算出自己的frame)
self.text_label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
}
- 设置tableView的cell估算高度
// 告诉tableView所有cell的估算高度(设置了估算高度,就可以减少tableView:heightForRowAtIndexPath:方法的调用次数)
self.tableView.estimatedRowHeight = 200;
- 在代理方法中计算cell的高度
XMGStatusCell *cell;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 创建一个临时的cell(cell的作用:根据模型数据布局所有的子控件,进而计算出cell的高度)
if (!cell) {
cell = [tableView dequeueReusableCellWithIdentifier:ID];
}
// 设置模型数据
cell.status = self.statuses[indexPath.row];
return cell.height;
}
- (CGFloat)height
{
// 强制布局cell内部的所有子控件(label根据文字多少计算出自己最真实的尺寸)
[self layoutIfNeeded];
// 计算cell的高度
if (self.status.picture) {
return CGRectGetMaxY(self.pictureImageView.frame) + 10;
} else {
return CGRectGetMaxY(self.text_label.frame) + 10;
}
}
第十节
1.iOS面试相关
https://github.com/lintaoSuper/trip-to-iOS
https://github.com/ipader/SwiftGuide
https://github.com/ChenYilong/iOSInterviewQuestions
stackoverflow(全英文, IT问答网站) 学习新东西
2. 数据刷新
- 添加数据
- 删除数据
- 更改数据
3.全局刷新方法(最常用)
[self.tableView reloadData];
// 屏幕上的所有可视的cell都会刷新一遍
4.局部刷新方法
- 添加数据
NSArray *indexPaths = @[
[NSIndexPath indexPathForRow:0 inSection:0],
[NSIndexPath indexPathForRow:1 inSection:0]
];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationRight];
- 删除数据
NSArray *indexPaths = @[
[NSIndexPath indexPathForRow:0 inSection:0],
[NSIndexPath indexPathForRow:1 inSection:0]
];
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationMiddle];
- 更新数据(没有添加和删除数据,仅仅是修改已经存在的数据)
NSArray *indexPaths = @[
[NSIndexPath indexPathForRow:0 inSection:0],
[NSIndexPath indexPathForRow:1 inSection:0]
];
[self.tableView relaodRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationMiddle];
5.左滑出现删除按钮
- 需要实现tableView的代理方法
/**
* 只要实现了这个方法,左滑出现Delete按钮的功能就有了
* 点击了“左滑出现的Delete按钮”会调用这个方法
*/
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// 删除模型
[self.wineArray removeObjectAtIndex:indexPath.row];
// 刷新
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
/**
* 修改Delete按钮文字为“删除”
*/
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
return @"删除";
}
6.左滑出现N个按钮
- 需要实现tableView的代理方法
/**
* 只要实现了这个方法,左滑出现按钮的功能就有了
(一旦左滑出现了N个按钮,tableView就进入了编辑模式, tableView.editing = YES)
*/
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
}
/**
* 左滑cell时出现什么按钮
*/
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewRowAction *action0 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"关注" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
NSLog(@"点击了关注");
// 收回左滑出现的按钮(退出编辑模式)
tableView.editing = NO;
}];
UITableViewRowAction *action1 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"删除" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
[self.wineArray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}];
return @[action1, action0];
}
7.进入编辑模式
// self.tabelView.editing = YES;
[self.tableView setEditing:YES animated:YES];
// 默认情况下,进入编辑模式时,左边会出现一排红色的“减号”按钮
8.在编辑模式中多选
// 编辑模式的时候可以多选
self.tableView.allowsMultipleSelectionDuringEditing = YES;
// 进入编辑模式
[self.tableView setEditing:YES animated:YES];
// 获得选中的所有行
self.tableView.indexPathsForSelectedRows;
第十一节
代理的使用步骤
-
定义一份代理协议
- 协议名字的格式一般是:类名 + Delegate
- 比如UITableViewDelegate
- 设计代理的细节
- 一般都是@optional(让代理可以有选择性去实现一些代理方法)
- 方法名一般都以类名开头
- 比如
- (void)scrollViewDidScroll:
- 比如
- 一般都需要将对象本身传出去
- 比如tableView的代理方法都会把tableView本身传出去
- 必须要遵守NSObject协议(基协议)
- 比如
@protocol XMGWineCellDelegate
- 比如
- 协议名字的格式一般是:类名 + Delegate
-
声明一个代理属性
- 代理的类型格式:id<协议> delegate
@property (nonatomic, weak) id delegate;
设置代理对象
代理对象遵守协议,实现协议里面相应的方法
-
当控件内部发生了一些事情,就可以调用代理的代理方法通知代理
- 如果代理方法是@optional,那么需要判断方法是否有实现,直接调用可能会报错
if ([self.delegate respondsToSelector:@selector(wineCellDidClickPlusButton:)]) {
[self.delegate wineCellDidClickPlusButton:self];
}
iOS监听某些事件的方法
-
通知(NSNotificationCenter\NSNotification)
- 任何对象之间都可以传递消息
- 使用范围
- 1个对象可以发通知给多个对象
- 1个对象可以接受多个对象发出的通知
- 要求:必须得保证通知的名字在发出和监听时是一致的
-
KVO
- 仅仅是能监听对象属性的改变(灵活度不如通知和代理)
-
代理
- 使用范围
- 1个对象只能设置一个代理(假设这个对象只有1个代理属性)
- 1个对象能成为多个对象的代理
- 使用范围
-
如何选择?
-
代理
比通知
规范 - 建议使用
代理
多于通知
,能使用代理尽量使用代理
-