11-自定义cell(2种方法)

第一种方法 :使用代码创建 分析:
(1)重写initWithStyle方法,在这个方法里创建控件或者尺寸约束(其实控件约束一般写在layoutSubviews里)。
(2)在layoutSubviews方法里设置尺寸
(3)重写模型的set方法

重写模型的set方法:
先声明模型类型,接受模型数据
@class XMGTopic; @interface XMGTopicCell : UITableViewCell /** 模型数据 */ @property (nonatomic, strong) XMGTopic *topic; @end

代码:
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { // 增加顶部的控件,并且设置约束 // ...// 增加底部的控件,并且设置约束 // ... } return self; }/* - (void)layoutSubviews { [super layoutSubviews]; // 设置顶部和底部控件的frame }*/- (void)setTopic:(XMGTopic *)topic { _topic = topic; // 设置顶部和底部控件的具体数据(比如文字数据、图片数据) } @end

在Controller里创建cell并导入数据也有2种方法:
第一种:不常用(最基本的创建cell方法)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *ID = @"cell"; // 缓存池去取 XMGTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { // 缓存池没有,创建 cell = [[XMGTopicCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } // 导入数据 cell.topic = self.topics[indexPath.row]; return cell; }

【11-自定义cell(2种方法)】第二种:通过注册,常用
注册的功能是:去缓存池取cell,若没有创建cell
/* cell的重用标识 */ static NSString * const XMGTopicCellId = @"XMGTopicCellId"; // 注册cell,registerClass:代码注册,registerNib:xib注册 [self.tableView registerClass:[XMGTopicCell class] forCellReuseIdentifier:XMGTopicCellId]; - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {XMGTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:XMGTopicCellId]; // 导入数据 cell.topic = self.topics[indexPath.row]; return cell; }

第二种方法:使用xib创建 分析:
(1)在- (void)awakeFromNib中进行控件设置
(2)重写模型的set方法
代码:
- (void)awakeFromNib { self.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mainCellBackground"]]; }- (void)setTopic:(XMGTopic *)topic { _topic = topic; // 顶部控件的数据 [self.profileImageView sd_setImageWithURL:[NSURL URLWithString:topic.profile_image] placeholderImage:[UIImage imageNamed:@"defaultUserIcon"]]; self.nameLabel.text = topic.name; self.passtimeLabel.text = topic.passtime; self.text_label.text = topic.text; // 底部按钮的文字 [self setupButtonTitle:self.dingButton number:topic.ding placeholder:@"顶"]; [self setupButtonTitle:self.caiButton number:topic.cai placeholder:@"踩"]; [self setupButtonTitle:self.repostButton number:topic.repost placeholder:@"分享"]; [self setupButtonTitle:self.commentButton number:topic.comment placeholder:@"评论"]; }

在Controller里注册cell并导入数据2种方法
第一种:通过mainBundle
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *ID = @"cell"; // 缓存池去取 XMGTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { // 缓存池没有,创建 cell = [[[NSBundle mainBundle] loadNibNamed:selfowner:nil options:nil] firstObject]; } // 导入数据 cell.topic = self.topics[indexPath.row]; return cell; }

第二种:通过注册加载cell
/* cell的重用标识 */ static NSString * const XMGTopicCellId = @"XMGTopicCellId"; // 注册cell UINib *nib = [UINib nibWithNibName:NSStringFromClass([XMGTopicCell class]) bundle:nil]; [self.tableView registerNib:nib forCellReuseIdentifier:XMGTopicCellId]; - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { XMGTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:XMGTopicCellId]; cell.topic = self.topics[indexPath.row]; return cell; }

技巧:因为通过mainBundle加载资源,使用地方很多,可以将其封装在UIView的分类里
代码:
+ (instancetype)xmg_viewFromXib; + (instancetype)xmg_viewFromXib { return [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil].firstObject; }

拓展:若有四种cell,其中有共同的部分,也有各自不同的部分,如何做? 分析:2种思路
第一种思路:父cell+子cell
分析:
  • 因为xib无法继承,所以需要用纯代码或者顶部一个xib,底部一个xib,添加到cell上
  • 在自定义子cell时,其它都相同,就是-setTopic时,需要先调用一下父控件的-setTopic方法,即将共同的顶部与底部先加载进来并设置好数据。
11-自定义cell(2种方法)
文章图片
1.png 子cell的自定义代码如下:
@implementation XMGVoiceCell- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { // 增加中间的声音控件,并且设置约束 // ...self.backgroundColor = [UIColor greenColor]; } return self; }/* - (void)layoutSubviews { [super layoutSubviews]; // 设置中间声音控件的frame }*/- (void)setTopic:(XMGTopic *)topic { [super setTopic:topic]; // 设置中间声音控件的具体数据(比如文字数据、图片数据) }@end

第二种思路:只用一种cell
11-自定义cell(2种方法)
文章图片
1.png 分析:
**(1)创建整体cell;
(2)中间内容: 自定义三种xib,分别是视频、语音、图片
(3)中间内容懒加载进整体cell中
**
11-自定义cell(2种方法)
文章图片
1.png
@class XMGTopic; @interface XMGTopicCell : UITableViewCell /** 模型数据 */ @property (nonatomic, strong) XMGTopic *topic;

/* 中间控件 */ /** 图片控件 */ @property (nonatomic, weak) XMGTopicPictureView *pictureView; /** 声音控件 */ @property (nonatomic, weak) XMGTopicVoiceView *voiceView; /** 视频控件 */ @property (nonatomic, weak) XMGTopicVideoView *videoView; @end@implementation XMGTopicCell #pragma mark - 懒加载 - (XMGTopicPictureView *)pictureView { if (!_pictureView) { XMGTopicPictureView *pictureView = [XMGTopicPictureView xmg_viewFromXib]; [self.contentView addSubview:pictureView]; _pictureView = pictureView; } return _pictureView; }- (XMGTopicVoiceView *)voiceView { if (!_voiceView) { XMGTopicVoiceView *voiceView = [XMGTopicVoiceView xmg_viewFromXib]; [self.contentView addSubview:voiceView]; _voiceView = voiceView; } return _voiceView; }- (XMGTopicVideoView *)videoView { if (!_videoView) { XMGTopicVideoView *videoView = [XMGTopicVideoView xmg_viewFromXib]; [self.contentView addSubview:videoView]; _videoView = videoView; } return _videoView; }#pragma mark - 初始化 - (void)awakeFromNib { self.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mainCellBackground"]]; }- (void)setTopic:(XMGTopic *)topic { _topic = topic; // 顶部控件的数据 UIImage *placeholder = [UIImage xmg_circleImageNamed:@"defaultUserIcon"]; [self.profileImageView sd_setImageWithURL:[NSURL URLWithString:topic.profile_image] placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { // 图片下载失败,直接返回,按照它的默认做法 if (!image) return; self.profileImageView.image = [image xmg_circleImage]; }]; self.nameLabel.text = topic.name; self.passtimeLabel.text = topic.passtime; self.text_label.text = topic.text; // 底部按钮的文字 [self setupButtonTitle:self.dingButton number:topic.ding placeholder:@"顶"]; [self setupButtonTitle:self.caiButton number:topic.cai placeholder:@"踩"]; [self setupButtonTitle:self.repostButton number:topic.repost placeholder:@"分享"]; [self setupButtonTitle:self.commentButton number:topic.comment placeholder:@"评论"]; // 最热评论 if (topic.top_cmt.count) { // 有最热评论 self.topCmtView.hidden = NO; NSDictionary *cmt = topic.top_cmt.firstObject; NSString *content = cmt[@"content"]; if (content.length == 0) { // 语音评论 content = @"[语音评论]"; } NSString *username = cmt[@"user"][@"username"]; self.topCmtLabel.text = [NSString stringWithFormat:@"%@ : %@", username, content]; } else { // 没有最热评论 self.topCmtView.hidden = YES; }// 中间的内容 if (topic.type == XMGTopicTypePicture) { // 图片 self.pictureView.hidden = NO; self.voiceView.hidden = YES; self.videoView.hidden = YES; } else if (topic.type == XMGTopicTypeVoice) { // 声音 self.pictureView.hidden = YES; self.voiceView.hidden = NO; self.voiceView.topic = topic; self.videoView.hidden = YES; } else if (topic.type == XMGTopicTypeVideo) { // 视频 self.pictureView.hidden = YES; self.voiceView.hidden = YES; self.videoView.hidden = NO; } else if (topic.type == XMGTopicTypeWord) { // 段子 self.pictureView.hidden = YES; self.voiceView.hidden = YES; self.videoView.hidden = YES; } }- (void)layoutSubviews { [super layoutSubviews]; if (self.topic.type == XMGTopicTypePicture) { // 图片 self.pictureView.frame = self.topic.middleFrame; } else if (self.topic.type == XMGTopicTypeVoice) { // 声音 self.voiceView.frame = self.topic.middleFrame; } else if (self.topic.type == XMGTopicTypeVideo) { // 视频 self.videoView.frame = self.topic.middleFrame; } }

问题1:如何避免同一个cell的高度计算多次,即让同一个cel的高度只计算一次? 答案1:利用模型属性,额外增加计算cell高度的属性,重写cellHeight的getter方法。在cellHeight方法里判断是否计算过行高,已经计算过就返回)
流程分析:
  • 当UITableView显示的时候,系统开始就会调用numberOfSectionsnumberOfRows方法,来知道行数和组数
  • 然后调用heightForRow方法,计算所有cell的高度,计算出来tableView的contentsize,确定tableView的滑动范围。
  • 再调用cellForRow方法,显示出cell
  • 当屏幕来回滑动的时候,heightForRow方法会调用的频繁
  • 解决上述问题,就定义一个模型属性,重写cellHeight的getter方法,已经计算过就返回
拓展:heightForRow这个方法的特点:
  • 默认情况下,每次刷新表格的时候,有多少数据,这个方法就一次调用多少次 ---如:若有100条数据,每次reloadData,这个方法就会调用100次。
  • 每当有cell进入屏幕范围,就会调用一次这个方法。
/* 额外增加的属性(并非服务器返回的属性,仅仅是为了提高开发效率) */ /** 根据当前模型计算出来的cell高度 */ @property (nonatomic, assign) CGFloat cellHeight; /** 中间内容的frame */ @property (nonatomic, assign) CGRect middleFrame; #import "XMGTopic.h"@implementation XMGTopic- (CGFloat)cellHeight { // 如果已经计算过,就直接返回 if (_cellHeight) return _cellHeight; // 文字的Y值 _cellHeight += 55; // 文字的高度 CGSize textMaxSize = CGSizeMake(XMGScreenW - 2 * XMGMarin, MAXFLOAT); _cellHeight += [self.text boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15]} context:nil].size.height + XMGMarin; // 中间的内容 if (self.type != XMGTopicTypeWord) { // 中间有内容(图片、声音、视频) CGFloat middleW = textMaxSize.width; CGFloat middleH = middleW * self.height / self.width; CGFloat middleY = _cellHeight; CGFloat middleX = XMGMarin; self.middleFrame = CGRectMake(middleX, middleY, middleW, middleH); _cellHeight += middleH + XMGMarin; }// 最热评论 if (self.top_cmt.count) { // 有最热评论 // 标题 _cellHeight += 21; // 内容 NSDictionary *cmt = self.top_cmt.firstObject; NSString *content = cmt[@"content"]; if (content.length == 0) { content = @"[语音评论]"; } NSString *username = cmt[@"user"][@"username"]; NSString *cmtText = [NSString stringWithFormat:@"%@ : %@", username, content]; _cellHeight += [cmtText boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:16]} context:nil].size.height + XMGMarin; }// 工具条 _cellHeight += 35 + XMGMarin; return _cellHeight; }@end

在Controller,返回每行模型对应的行高(在模型里,如果已经计算好,就会返回,若没有计算行高,就会继续执行计算行高)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { XMGTopic * topic=self.topics[indexPath.row]; return topic.cellHeight; }

    推荐阅读