iOS小技能:cell的重用原理及自定义cell
Posted #公众号:iOS逆向
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS小技能:cell的重用原理及自定义cell相关的知识,希望对你有一定的参考价值。
引言
ios开发中,要实现表格数据展示,做常用的做法就是使用UITableView。
I UITableViewCell的简介
1.1 cell的结构
UITableView的每一行都是一个UITableViewCell
- cell的初始化: 通过datasource的tableView: cellForRowAtIndexPath: 方法来初始化每一行
- UITableViewCell内部有一个默认的自视图ContentView :
ContentView是UITableViewCell所显示内容的父视图,可显示一些辅助视图(辅助视图的作用是显示一个表示动作的图标);contentView 默认有3个子视图(textLabel、detailTextLabel、UIImageView)3. UITableViewCell的UITabelCellStyle属性:用于决定使用contentView的哪些子视图,以及这个视图在contentView中的位置4. cell的结构
1.2 cell的重用原理
- 重用原理
当滚动列表时,部分cell会移出窗口,UITableView会将窗口外的cell放入一个等待重用的对象池;当UITableView要求datasource返回cell的时候,datasource会先查看这个对象池是否有未使用的cell,若有,datasource会用新的数据配置这个cell,并返回给UITableVIew重新显示到窗口中,从而避免创建新的对象。2. 解决一个TableView同时拥有不通类型的cell的问题解决方案:在初始化cell的时候,传入一个特定的“字符串标识”(通常使用cell的类名)来给cell的reuseIdentifier属性赋值;当UITableView 要求datasource返回cell的时候,此时就利用reuseIdentifier属性到对象词中查找对应类型的cell对象,若找到就重用,否则利用这个reuseIdentifier属性来实例化一个cell对象3. cell重用的例子:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
// 1.定义一个cell的标识
static NSString *ID = @"mjcell";
// 2.从缓存池中取出cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 3.如果缓存池中没有cell
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
// 4.设置cell的属性...
return cell;
- 单元格循环引用概念
II 使用xib封装一个View的步骤
- new一个xib文件来描述一个View的内部结构
- new一个自定义的视图类(继承自xib根对象的class),类名通常与xib文件名保存一致
- 将xib中的控件与自定义视图类的.m文件建立连线 (建立连线之前,对根对象的class指定为刚刚新建的控件->在xi b属性面板指定可重用标识符
- 提供一个类方法,返回一个创建好的自定义视图类(屏蔽从xib加载的过程)
5, 提供一个模型属性让外界传递模型数据,重写模型属性的setter方法(将模型数据展示到对应的子控件上面)
III delegate
父控件(视图控制器)监听子控件的事件,当子控件发生某些事情的时候,通知父控件工作--备注:父控件通知子控件工作,直接调用子控件的方法即可。
如果使用强引用,将造成循环的强引用-->儿子只能对父亲进行弱引用。
@property (nonatomic,weak) id<HSGroupBuyingFooterViewDelegate> delegate;//在oc中,只有没有强用的时候,才会被立即释放;一旦自定义视图称为视图控制器的视图包含,极自定义视图为视图控制器的儿子时,且视图控制器为自定义视图(儿子)的代理,此时如果代理是强引用,将造成循环的强引用,”你中有我,我中有你“。--永远呆在内存
3.1 delegate的使用场合
- 对象A内部发生了一些事情,想通知对象B,对象A想传递数据给对象B
- 对象B想监听对象A内部发生了什么事情
- 对象A想在自己的方法内部调用对象B的某些方法,并且对象A不能对对象B有耦合依赖
3.2 使用delegate的步骤
- 搞清楚谁 是 谁的 delegate
- 定义代理协议(协议名称的命名规范:控件类名+Delegate)
- 定义代理方法:
*代理方法一般都定义为optional
*代理方法名称都以空间名开头;代理方法至少有个参数,用于将控件本事传递出去
4. 设置代理对象(代理对象遵守协议,并实现协议方法)
5. 在恰当的时刻调用delegate的协议方法,来通知delegate发生了什么事情(在调用之前判断代理是否实现了该代理方法)
IV 通过代码自定义cell
- new一个继承自UITableViewCell的类
- 重写initWithStyle: reuseIndentifier: 方法(对子控件的属性进行一次性赋值)
--有些属性只须设置一次,例如字体、固定的图片
3. 并添加需要显示的子控件到contentView中
4. 提供数据模型、frame模型
数据模型用于存放文字、图片数据;frame模型存放数据模型、所有子控件的frame、cell的高度
5. cell拥有一个frame模型
6. 重写frame模型的setter方法(设置子控件的显示数frame);对frame的对象实例化采用懒加载方法,即进行getter方法的重写
//
// HSStatusCell.h
@interface HSStatusCell : UITableViewCell
//自定义视图的现实的数据来源于模型,即使用模型装配自定义视图的显示内容
@property (nonatomic,strong) HSStatus *status;//视图对应的模型,是视图提供给外界的接口
/**
通过数据模型设置视图内容,可以让视图控制器不需要了解视图的细节
*/
+ (instancetype) tableVieCellwWithStatus:(HSStatus *) status tableView:(UITableView *)tableView;//使用类方法获取自定义视图,参数用于视图的数据装配
//
+ (instancetype) tableVieCellwWittableView:(UITableView *)tableView;//使用类方法获取自定义视图
@end
- 设置位置
- (void)setStatus:(HSStatus *)status
_status = status;
[self settingData];
//设置位置
[self settingFrame];
- (void) settingData
//设置位置
[self.textView setText:self.status.text];
[self.nameView setText:self.status.name];
[self.iconView setImage:self.status.iconImage];
if (self.status.picture.length > 0)
[self.pictureView setImage:self.status.pictureImage];
[self.pictureView setHidden:NO];
else
[self.pictureView setImage:nil];//没有配图的时候,清空图片信息--cell重用的时候,对于可选视图要进行处理
[self.pictureView setHidden:YES];
if (self.status.vip)
[self.vipView setImage:self.status.vipImage];
[self.vipView setHidden:NO];//显示VIP视图
[self.nameView setTextColor:[UIColor redColor]];
else
[self.vipView setImage:nil];//不是VIP的时候,清空VIP标识--cell重用的时候,针对可选视图进行特殊处理
[self.vipView setHidden:YES];
[self.nameView setTextColor:[UIColor blackColor]];
/**
设置位置信息
*/
- (void) settingFrame
//定义间距
CGFloat padding =10;
CGFloat iconX = padding;
CGFloat iconY = padding;
CGFloat iconWidth = 30;
CGFloat iconHeiht = 30;
[self.iconView setFrame:CGRectMake(iconX, iconY, iconWidth, iconHeiht)];
//设置昵称,昵称的大小由文字的长度决定
/**
1.boundingRectWithSize 方法计算给定文本所占用的区域
2.options: 计算多行的的准确高度需要传入NSStringDrawingUsesLineFragmentOrigin
3、attributes 指定字体相关属性;UIKit框架中的第一个头文件 NSAttributedString.h
*/
NSDictionary *nameDict = @NSFontAttributeName:KnameFont;
CGRect nameFrame = [self.status.name boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:nameDict context:nil];//返回一个x,y,都为0 的CGRect
nameFrame.origin.x = CGRectGetMaxX(self.iconView.frame)+padding;
nameFrame.origin.y = padding+(CGRectGetHeight(self.iconView.frame)-CGRectGetHeight(nameFrame))*0.5;
[self.nameView setFrame:nameFrame];
//设置VIP标识的frame
[self.vipView setFrame:CGRectMake(CGRectGetMaxX(self.nameView.frame)+padding, CGRectGetHeight(self.nameView.frame), 14, 14)];
//设置文本内容的frame
NSDictionary Cell的重用原理