自定义不等高cell,详解步骤
一.创建一个继承与UITableViewCell的类
在让这个类的.h文件拥有一个模型属性,在.m文件拥有子控件的类型属性,这样就可以在layoutSubviews
和set
方法中拿到这些子控件,给它设置frame,赋值;
1.在下面方法中添加子控件
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// 添加子控件
*******
}
return self;
}
2.在layoutSubviews
方法中设置frame;
- (void)layoutSubviews
{
[super layoutSubviews];
self.iconImageView.frame = self.status.iconFrame;
self.nameLabel.frame = self.status.nameFrame;
*******
}
3.在模型属性的set方法中给子控件设置数据
- (void)setStatus:(DXStatus *)status
{
_status = status;
if (self.status.picture) {
self.pictureImageView.hidden = NO;
self.pictureImageView.image = [UIImage imageNamed:status.picture];
}else {
self.pictureImageView.hidden = YES;
}
******
注意
:因为涉及到根据标识符循环利用cell,所以在给子控件中赋值时候要判断这个cell是否有这个子控件,有if 就一定有else.
并且注意
,是else
和else if
区别,else
意思是除了if
外的其他情况,而else if
是另外一种判断;表示还有其他可能情况;
二.新建一个模型
- 1.给模型添加子控件的frame属性,用于传递给新建的类
- 2.给模型添加plist文件中相对应的属性;
- 3.给模型添加子控件的frame属性;
- 4.给模型添加(cellHeight)cell行高属性;
- 5.在模型中重写cellHeight的get方法,并在里面计算cell的行高;
比如:
@property (nonatomic, copy) NSString *picture;
@property (nonatomic, assign) BOOL vip;
@property (nonatomic,assign) CGFloat cellHeight;
@property (nonatomic,assign) CGRect iconFrame;
声明属性需要特别注意:声明属性,起名字注意,不要跟系统自带的属性名字一样,比如在UITableViewCell中,它又一个imageView,那么你自定义UIImageView,不要用imageView作为属性名字,可以用iconImageView,如果你用这个imageView,可能会覆盖系统自带的属性,或者会出现问题,比如我用`UILabel *textLabel = [[UILabel alloc] init]`;那么UILabel系统内部有一个textLabel,这样会导致自己在给这个控件设置frame时候,出现和系统混乱问题,;
但是你可以用titleLabel,作为一个UILabel类型的名字,虽然UIButton内部有一个titleLabel属性,但是你现在没有用到UIButton
起名字要注意,一般要在后面加上控件的名称,比如声明一个UILabel类型的的属性,他名字最好是nameLabe,l
三.在控制器中需要做
- 1.更改viewController继承为UITableViewController
- 2.删除storyboard,添加UITableViewController控制器,更改它类为viewController;
- 3.让控制器拥有一个数组属性,用来装模型;
- 4.懒加载--数组属性重写get方法,在这个方法判断,如果属性为空,就加载plist文件(mainBundle -->数组-->遍历数组-->新模型数组-->赋值给当前属性 ),
- 5.实现数据源方法
- 1.一共有多少行
tableView: numberOfRowsInSection:
- 2.每一行长什么样子,并传递
对应的
模型数据给cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"status";
// 1.从缓存池中取
DXStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 2.如果缓存池没有,自己创建
if (!cell) {
cell = [[DXStatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
// 3.传递对应的模型数据给cell
cell.status = self.statuses[indexPath.row];
return cell;
}
6.实现代理方法,返回对应行高;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
DXStatus *status = self.statuses[indexPath.row];
return status.cellHeight;
}
难点:为什么要在模型中计算子控件和cell的高度?
因为cell的高度是由代理方法设置的,而这个代理方法调用顺序先于layoutSubviews这个方法,而我们的cell高度又是依据子控件的高度而不同计算出来,因此从layoutSubviews这个方法中计算子控件frame,拿到cell高度,不能给代理方法用,因为调用顺序代理方法调用顺序先于layoutSubviews这个方法,因此在模型中给它一个cell高度属性,在属性set方法中计算子控件,算出每一个cell高度,在代理方法中,利用(indexPath.row)取出对应的模型对象,直接返回模型对象的cell属性,那么就会调用模型中cellHeight属性的get方法,就能拿到cellHeight;
技巧一:如果有的cell有图片,有的没有图片,先统一创建,然后再设置frame的时候进行判断,有的才给设置frame;
在set方法赋值的时候,也要进行判断这个cell是否有这个控件,如果有,就让它设置为显示,并进行赋值,如果没有,就让这个控件设置隐藏,不给它赋值;
技巧二:数据源方法的快速书写
一共有多少行这个方法tableView:numberOfRowsInSection:
先写返回值- (NSInteger)tableView
就可过滤掉很多方法
每一行长什么样子
tableView: cellForRowAtIndexPath:
先写返回值 - (UITableViewCell *)tableView
就可过滤掉很多方法
小知识点
1.contentView 和cell高度就差1,因为要显示分割线;
2.static NSString *ID = @“wine”;
用static修饰局部变量,这个局部变量就只创建一次,只分配一次内存,要不加static修饰字符串,每次调用这个方法就会重新创建一个局部变量;
不建议写外边,全局变量,会访问可以修改,但是注册某个标识,应该使用全局变量;