美文网首页
11-自定义cell(2种方法)

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

作者: 小胖子2号 | 来源:发表于2017-05-07 19:25 被阅读22次

    第一种方法 :使用代码创建

    分析:
    (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方法,即将共同的顶部与底部先加载进来并设置好数据。
    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

    1.png

    分析:

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

    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种方法)

          本文链接:https://www.haomeiwen.com/subject/vmsutxtx.html