「这是我参与2022首次更文挑战的第35天,活动详情查看:2022首次更文挑战」。
引言
iOS开发中,要实现表格数据展示,做常用的做法就是使用UITableView。
I UITableViewCell的简介
1.1 cell的结构
UITableView的每一行都是一个UITableViewCell
- cell的初始化: 通过datasource的tableView: cellForRowAtIndexPath: 方法来初始化每一行
- UITableViewCell内部有一个默认的自视图ContentView : ContentView是UITableViewCell所显示内容的父视图,可显示一些辅助视图(辅助视图的作用是显示一个表示动作的图标);contentView 默认有3个子视图(textLabel、detailTextLabel、UIImageView)
- UITableViewCell的UITabelCellStyle属性: 用于决定使用contentView的哪些子视图,以及这个视图在contentView中的位置
- cell的结构
1.2 cell的重用原理
- 重用原理 当滚动列表时,部分cell会移出窗口,UITableView会将窗口外的cell放入一个等待重用的对象池; 当UITableView要求datasource返回cell的时候,datasource会先查看这个对象池是否有未使用的cell,若有,datasource会用新的数据配置这个cell,并返回给UITableVIew重新显示到窗口中,从而避免创建新的对象。
- 解决一个TableView同时拥有不通类型的cell的问题 解决方案: 在初始化cell的时候,传入一个特定的“字符串标识”(通常使用cell的类名)来给cell的reuseIdentifier属性赋值; 当UITableView 要求datasource返回cell的时候,此时就利用reuseIdentifier属性到对象词中查找对应类型的cell对象,若找到就重用,否则利用这个reuseIdentifier属性来实例化一个cell对象
- 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 *代理方法名称都以空间名开头;代理方法至少有个参数,用于将控件本事传递出去
- 设置代理对象(代理对象遵守协议,并实现协议方法)
- 在恰当的时刻调用delegate的协议方法,来通知delegate发生了什么事情(在调用之前判断代理是否实现了该代理方法)
IV 通过代码自定义cell
- new一个继承自UITableViewCell的类
- 重写initWithStyle: reuseIndentifier: 方法(对子控件的属性进行一次性赋值) --有些属性只须设置一次,例如字体、固定的图片
- 并添加需要显示的子控件到contentView中
- 提供数据模型、frame模型 数据模型用于存放文字、图片数据;frame模型存放数据模型、所有子控件的frame、cell的高度
- cell拥有一个frame模型
- 重写frame模型的setter方法(设置子控件的显示数frame);对frame的对象实例化采用懒加载方法,即进行getter方法的重写
//
// HSStatusCell.h
#import <UIKit/UIKit.h>
#import "HSStatus.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 *textDict = @{NSFontAttributeName:KtextFont};
CGRect textFrame = [self.status.text boundingRectWithSize:CGSizeMake(300, MAXFLOAT) options: NSStringDrawingUsesLineFragmentOrigin attributes:textDict context:nil];
textFrame.origin.x = padding;
textFrame.origin.y = CGRectGetMaxY(self.iconView.frame)+padding ;
[self.textView setFrame:textFrame];
//设置picture的frame
[self.pictureView setFrame:CGRectMake(padding,CGRectGetMaxY(self.textView.frame)+padding, 100, 100)];
}
- 计算行高
#pragma mark - tableView delegate方法
#if 1
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
/**
问题背景: 执行此方法的时候,cell还没被实例化;行高实在实例化cell之后,设置cell对象的模型属性status的时候计算的
解决方案:得到模型-》确定行高
*/
//在cell实例化之前,获取行高-》先获取到模型
HSStatus *statuse = self.stautses[indexPath.row];
//开始计算行高
// CGFloat height = padding+iconView.frame.height+padding+textView.frame.height+pictureView.frame.height+padidng;
CGFloat iconViewHeight = 30;
CGFloat pictureViewHeight = 100;
CGFloat padding = 10;
CGFloat textViewHeight = [statuse.text boundingRectWithSize:CGSizeMake(300, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil].size.height;
CGFloat height =iconViewHeight+textViewHeight+((statuse.picture.length >0 ? pictureViewHeight+padding: 0))+padding*2;
return height;
}
#endif
see also
🍅 联系作者: iOS逆向(公号:iosrev)
🍅 作者简介:CSDN 博客专家认证🏆丨全站 Top 50、华为云云享专家认证🏆、iOS逆向公号号主
🍅 简历模板、技术互助。关注我,都给你。