1.简介
代码:https://github.com/xufeng79x/DynamicChangeableCell
微博界面,微信和QQ聊天界面,这些界面的布局大都不确定,且每一条消息的高度也不一样。在TableView的代理UITableViewDelegate的heightForRowAtIndexPath中可以动态的定义每一行的高度,但是我们需要得到cell的内容后才能最终确认当前的cell到底需要多少高度,而得到cell的方法是代理UITableViewDataSource的cellForRowAtIndexPath方法,从断点debug手段来看,系统都是先调用heightForRowAtIndexPath方法然后在调用cellForRowAtIndexPath方法,所以从调用顺序来看,在heightForRowAtIndexPath之前就需要将高度信息计算出来。
2.目标
代码:https://github.com/xufeng79x/DynamicChangeableCell
如下图模拟了一个简单的微博界面,此界面极大简化了复杂度,只包含一个头像和文本内容。但是对一个demo用于解释此场景已经足够了。
3.实现
对于很多变化多样的cell来说,固定布局的xib活着直接storyborad已经无法满足要求,cell中高度,cell中各个控件的布局或者是否显示等是可变的。因此我们我们唯有使用代码去
创建控件并根据具体条件去设置他们。
关键步骤讲解:
1.创建自定义cell:
重用cell,并且重写构造方法,在构造方法中去创建各个控件,但是其并不包含frame信息。在微博或者qq等场景中控件的frame都不是固定的。
所以我们不能通过xib那样的方式去实现而只能使用代码的动态创建来完成。
/** * 创建cell * * @param tableView 宿主table * * @return cell */ +(instancetype) webCellWithTableView:(UITableView *)tableView { // 根据可ID进行cell的可重用查找 static NSString *reuseId = @"webcell"; XFWebTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId]; if (!cell) { cell = [[self alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseId]; } return cell; } /** * 重写父类的方法 * * @param style 重写父类初始化方法并创建子类的控件 * @param reuseIdentifier 重用ID * * @return cell */ -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { // 开始创建子控件 // 头像 UIImageView *picView = [[UIImageView alloc] init]; [self.contentView addSubview:picView]; self.picView = picView; // 内容 UILabel *textView = [[UILabel alloc] init]; textView.font = [UIFont systemFontOfSize:XF_TEXT_FONT]; textView.numberOfLines = ; [self.contentView addSubview:textView]; self.textView = textView; } return self; }
2.创建含有frame信息的模型类,其中根据数据模型对象来创建各个控件的frame信息
#import <UIKit/UIKit.h> // 字体大小设定 #define XF_TEXT_FONT 15 @class XFWebModel; /** * 微博cell的布局信息和数据信息集合 */ @interface XFWebFrame : NSObject // 当前cell的数据 @property(nonatomic, strong) XFWebModel *webInfo; // 当前cell所需要的高度 @property(nonatomic,assign,readonly) NSInteger cellHight; // 头像空间的frame @property(nonatomic,assign,readonly) CGRect picFrame; // 正文控件frame @property(nonatomic, assign,readonly) CGRect textFrame; /** * 类构造方法,用于返回每一行微博数据和布局列表 * * @return 布局信息和微博信息 */ +(NSMutableArray *)webFrames; @end
3.在Controller中起plist中加载数据的时候就直接创建了含有frame信息的对象列表:
/** * 懒加载web数据 * * @return 微博数据 */ - (NSMutableArray *)webFrames { if (!_webFrames) { _webFrames = [XFWebFrame webFrames]; } return _webFrames; }
4.在上述加载过程中,我们需要计算每一条微博的cell的布局信息:
/** * 记载数据并计算布局 * * @param webInfo 记载数据并计算布局 */ -(void) setWebInfo:(XFWebModel *)webInfo{ // 数据设定 _webInfo = webInfo; // 开始计算布局 [self calculateFrameWithInfo:webInfo]; } /** * 计算布局 * * @param webInfo 计算每一行微博的布局 */ - (void)calculateFrameWithInfo:(XFWebModel *)webInfo { CGFloat margin = ; // 头像 CGFloat picW = ; CGFloat picH = ; CGFloat picX = margin; CGFloat picY = margin; _picFrame = CGRectMake(picX, picY, picW, picH); // 正文 CGSize textSize = [self sizeWithText:self.webInfo.content maxSize:CGSizeMake(, MAXFLOAT) fontSize:XF_TEXT_FONT]; CGFloat textX = picX; CGFloat textY = CGRectGetMaxY(self.picFrame) + margin; _textFrame = CGRectMake(textX, textY, textSize.width, textSize.height); // 计算行高 _cellHight = CGRectGetMaxY(self.textFrame) + margin; }
5.在Controller中每一条微博的cell布局和内容信息都直接传递给自定义cell对象,而高度已经在上述懒加载过程都已经计算出来直接使用即可。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 将微博信息与其布局信息传递至自定义cell XFWebTableViewCell *cell = [XFWebTableViewCell webCellWithTableView:tableView]; cell.webFrame = self.webFrames[indexPath.row]; return cell; } - (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { //高度已经计算完成,直接使用 XFWebFrame *frame = self.webFrames[indexPath.row]; return frame.cellHight; }
6.在自定义cell中根据Controller传入的frame对象信息在将各个控件的frame进行调整并将微博内容
// 接受微博信息和布局信息 - (void)setWebFrame:(XFWebFrame *)webFrame { _webFrame = webFrame; // 设置内容 self.picView.image = [UIImage imageNamed:webFrame.webInfo.photo]; self.textView.text = webFrame.webInfo.content; // 设置布局 self.picView.frame = webFrame.picFrame; self.textView.frame = webFrame.textFrame; }
4.总结:
本文主要说明对于动态cell(高度,控件内容,frame每一个cell都不一致)需要我们在得到数据后在直接将布局高度等信息计算出来,在加载cell中将这些信息传入自定义cell中设定相应的frame。