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;
}

第二种:通过注册,常用
注册的功能是:去缓存池取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:self  owner: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张图片
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种方法)_第2张图片
1.png

分析:

**(1)创建整体cell;
(2)中间内容: 自定义三种xib,分别是视频、语音、图片
(3)中间内容懒加载进整体cell中
**

11-自定义cell(2种方法)_第3张图片
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;
}

你可能感兴趣的:(11-自定义cell(2种方法))