UI 一一 自定义不等高cell (Frame方式)


在讲自定义不等高cell之前先了解一下静态cell 和 动态cell.

动态cell   和 静态cell 都是在UITableViewController的storyboard中操作!

动态cell : 在storyboard 创建一个UITableView的控制器,默认就是动态的cell,所谓动态cell就是在程序运行后,cell的个数以及

cell中显示的内容都是由数据源决定的.


静态cell: 显示的内容可以在storyboard设置,并定死,在storyboard中设置的什么内容,运行出来就是什么内容.

(比如手机的设置界面就可以用静态cell来做)


注意:  静态的cell也可以使用代理方法去监听静态cell的事件

  在静态的cell中,也可以自定义cell,不过需要把style改为custom,如果使用系统的样式,可以把style改为basic


通过纯代码的方式自定义不等高cell效果图如下:



从上面的效果图中可以看出:

每一个cell的高度都是不同的,计算cell的高度也是本文中的一个重点及难点!


项目需求: 
以微博为例,当别人发状态时,有的人发文字,有的人发文字和图片,那么如何实现这种方式呢?


项目思路: 
在UITableViewCell中的系统样式中不能满足项目所需,那么我们首先得想,通过自定义cell的方式来完成,至于高度不等,那么我们可以在
每一个cell中通过判断哪个控件是最后一个,然后就取它的高度然后和cell底部间留有间距就完成了

项目实现过程: 
1.创建项目,导入图片及plist文件 
2.创建数据模型,用来进行plist字典数据转模型 
3.创建一个继承自UITableViewCell的文件用来设置自定义cell以及数据的重写 
4.在控制器中遵守UITableViewDataSourse协议,创建代理对象,实现代理方法等。

给模型增加frame数据

  • 所有子控件的frame
  • cell的高度
@interface XMGStatus : NSObject
/**** 文字\图片数据 ****/
// .....

/**** frame数据 ****/
/** 头像的frame */
@property (nonatomic, assign) CGRect iconFrame;
// .....
/** cell的高度 */
@property (nonatomic, assign) CGFloat cellHeight;
@end
  • 重写模型cellH属性的get方法
- (CGFloat)cellHeight
{
    if (_cellHeight == 0) {
        // ... 计算所有子控件的frame、cell的高度
    }
    return _cellHeight;
}

在控制器中

  • 实现一个返回cell高度的代理方法
    • 在这个方法中返回indexPath位置对应cell的高度
/**
 *  返回每一行cell的具体高度
 */
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    XMGStatus *status = self.statuses[indexPath.row];
    return status.cellH;
}
  • 给cell传递模型数据
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"status";
    // 访问缓存池
    
ZYStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    // 设置数据(传递模型数据)
    cell.status = self.statuses[indexPath.row];

    return cell;
}

新建一个继承自UITableViewCell的子类,比如ZYStatusCell

@interface ZYStatusCell : UITableViewCell
@end

在ZYStatusCell.m文件中

  • 重写-initWithStyle:reuseIdentifier:方法
    • 在这个方法中添加所有可能显示的子控件
    • 给子控件做一些初始化设置(设置字体、文字颜色等)
/**
 *  在这个方法中添加所有的子控件
 */
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        // ......
    }
    return self;
}

在ZYStatusCell.h文件中提供一个模型属性,比如ZYStatus模型

@class ZYStatus;

@interface XMGStatusCell : UITableViewCell
/** 团购模型数据 */
@property (nonatomic, strong)ZYStatus *status;
@end

在ZYStatusCell.m文件中重写模型属性的set方法

  • 在set方法中给子控件设置模型数据
- (void)setStatus:(ZYStatus *)status
{
    _status = status;

    // .......
}

重写-layoutSubviews方法

  • 一定要调用[super layoutSubviews]
  • 在这个方法中设置所有子控件的frame
/**
 *  在这个方法中设置所有子控件的frame
 */
- (void)layoutSubviews
{
    [super layoutSubviews];

    // ......
}

具体代码如下;

ZYStatus文件:

#import <UIKit/UIKit.h>

// 模型数据
@interface ZYStatus : NSObject

/** 图像 */
@property (nonatomic, copy) NSString *icon;

/** 昵称 */
@property (nonatomic, copy) NSString *name;

/** 正文(内容) */
@property (nonatomic, copy) NSString *text;

/** VIP */
@property (nonatomic, assign, getter=isVip) BOOL vip;

/** 配图 */
@property (nonatomic, copy) NSString *picture;


/** 图像的frame */
@property(nonatomic,assign)CGRect iconFrame;

/** 昵称的frame */
@property(nonatomic,assign)CGRect nameFrame;

/** vip的frame */
@property(nonatomic,assign)CGRect vipFrame;

/** 正文frame */
@property(nonatomic,assign)CGRect textFrame;

/** 配图的frame */
@property(nonatomic,assign)CGRect pictureFrame;


/** cell的高度 */
@property(nonatomic,assign)CGFloat cellHeight;


@end


@implementation ZYStatus

- (CGFloat)cellHeight
{
    if (_cellHeight == 0) {
        // 设置子控件的frame
        CGFloat space = 10;
        
        /** 图像 */
        CGFloat iconX = space;
        CGFloat iconY = space;
        CGFloat iconWH = 30;
        self.iconFrame = CGRectMake(iconX, iconY, iconWH, iconWH);
        
        /** 昵称 */
        CGFloat nameX = CGRectGetMaxX(self.iconFrame) + space;
        CGFloat nameY = iconY;
        NSDictionary *nameAtt = @{NSFontAttributeName : [UIFont systemFontOfSize:16]};
        //计算昵称文字的尺寸
        
        CGSize nameSize = [self.name sizeWithAttributes:nameAtt];
        CGFloat nameW = nameSize.width;
        CGFloat nameH = nameSize.height;
        self.nameFrame = CGRectMake(nameX, nameY, nameW, nameH);
        
        /** vip */
        if (self.isVip) {
            CGFloat vipX = CGRectGetMaxX(self.nameFrame) + space;
            //        CGFloat vipWH = 14;
            //        CGFloat vipY = nameY + (nameH - vipWH) * 0.5;
            CGFloat vipW = 14;
            CGFloat vipH = nameH;
            CGFloat vipY = nameY;
            self.vipFrame = CGRectMake(vipX, vipY, vipW, vipH);
        }
        
        /** 正文 */
        CGFloat textX = iconX;
        CGFloat textY = CGRectGetMaxY(self.iconFrame) + space;
        CGFloat textW = [UIScreen mainScreen].bounds.size.width - 2 *space;
        NSDictionary *textAtt = @{NSFontAttributeName : [UIFont systemFontOfSize:14]};
        // 计算文字的高度,需要知道文字从什么位置开始换行,且文字字数不限定(高度不限制)
        CGSize textSize = CGSizeMake(textW, MAXFLOAT);
        //    CGFloat textH = [self.status.text sizeWithFont:ZYTextFont constrainedToSize:textSize].height;
        CGFloat textH = [self.text boundingRectWithSize:textSize options:NSStringDrawingUsesLineFragmentOrigin attributes:textAtt context:nil].size.height;
        
        self.textFrame = CGRectMake(textX, textY, textW, textH);
        
        /** 配图 */
        if (self.picture) {
            CGFloat pictureWH = 100;
            CGFloat pictureX = iconX;
            CGFloat pictureY = CGRectGetMaxY(self.textFrame) + space;
            self.pictureFrame = CGRectMake(pictureX, pictureY, pictureWH, pictureWH);
            
            _cellHeight = CGRectGetMaxY(self.pictureFrame) + space;
        }else{
            _cellHeight = CGRectGetMaxY(self.textFrame) + space;
        }

    }
    return _cellHeight;
}

@end

ZYStatusCell文件

#import <UIKit/UIKit.h>
@class  ZYStatus;
@interface ZYStatusCell : UITableViewCell

/** 微博模型数据 */
@property(nonatomic,strong)ZYStatus *status;

@end

#import "ZYStatus.h"

#define ZYTextFont [UIFont systemFontOfSize:14]
#define ZYNameFont [UIFont systemFontOfSize:16]
@interface ZYStatusCell ()

/** 图像 */
@property (nonatomic, weak) UIImageView *iconImageView;
/** 昵称 */
@property (nonatomic, weak) UILabel *nameLabel;
/** vip */
@property (nonatomic, weak) UIImageView *vipImageView;
/** 正文 */
@property (nonatomic, weak) UILabel *text_Label;
/** 配图 */
@property (nonatomic, weak) UIImageView *pictureImageView;

@end

@implementation ZYStatusCell


//1. 添加子控件的原则:把所有有可能显示的子控件都先添加进去
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        // 添加子控件
        
        /** 图像 */
        UIImageView *iconImageView = [[UIImageView alloc] init];
        [self.contentView addSubview:iconImageView];
        self.iconImageView = iconImageView;
        
        /** 配图 */
        UIImageView *pictureImageView = [[UIImageView alloc] init];
        [self.contentView addSubview:pictureImageView];
        self.pictureImageView = pictureImageView;
        
        /** vip */
        UIImageView *vipImageView = [[UIImageView alloc] init];
        // 设置vip图片的内容模式
        vipImageView.contentMode = UIViewContentModeCenter;
        
        [self.contentView addSubview:vipImageView];
        self.vipImageView = vipImageView;
        
        /** 昵称 */
        UILabel *nameLabel = [[UILabel alloc] init];
        // 文字的大小和下面保持一致
        nameLabel.font = ZYNameFont;
        [self.contentView addSubview:nameLabel];
        self.nameLabel = nameLabel;
        
        /** 正文 */
        UILabel *text_Label = [[UILabel alloc] init];
        text_Label.numberOfLines = 0;
        text_Label.font = ZYTextFont;
        [self.contentView addSubview:text_Label];
        self.text_Label = text_Label;
    }
    
    return  self;
}

//2. 给子控件设置frame
- (void)layoutSubviews
{
    [super layoutSubviews];
    
    self.iconImageView.frame = self.status.iconFrame;
    self.nameLabel.frame = self.status.nameFrame;
    self.vipImageView.frame = self.status.vipFrame;
    self.text_Label.frame = self.status.textFrame;
    self.pictureImageView.frame = self.status.pictureFrame;
    
}

//3. 给子控件赋值
- (void)setStatus:(ZYStatus *)status
{
    _status = status;
    // 赋值
    self.iconImageView.image = [UIImage imageNamed:status.icon];
    self.nameLabel.text = status.name;
    
    if (status.isVip) {
        self.vipImageView.hidden = NO;
        // 有if就有else,如果在这里写字体颜色,会出现数据紊乱,不是会员的用户的名称也是黄色
        self.nameLabel.textColor = [UIColor orangeColor];
        self.vipImageView.image = [UIImage imageNamed:@"vip"];
    }else{
        self.vipImageView.hidden = YES;
        self.nameLabel.textColor = [UIColor blackColor];
    }
    
    self.text_Label.text = status.text;
    
    if (status.picture) {
        self.pictureImageView.hidden = NO;
        self.pictureImageView.image = [UIImage imageNamed:status.picture];
    }else{
        self.pictureImageView.hidden = YES;
    }
    
}


@end

ViewController 文件

#import "ViewController.h"
#import "MJExtension.h"
#import "ZYStatusCell.h"
#import "ZYStatus.h"

@interface ViewController ()

/** 所有的微博 */
@property (nonatomic,strong) NSArray *statuses;

@end

@implementation ViewController

- (NSArray *)statuses
{
    if (_statuses == nil) {
        //加载plist中的数据到,模型ZYStatus中
        _statuses = [ZYStatus mj_objectArrayWithFilename:@"statuses.plist"];
    }
    
    return _statuses;
}


NSString *ID = @"status";
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.tableView.rowHeight = 250;
    
    // 注册
    [self.tableView registerClass:[ZYStatusCell class] forCellReuseIdentifier:ID];

}

#pragma -mark 数据源方法

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.statuses.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 到缓存池中查看是否有可循环利用的cell
    ZYStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
    // 传递模型数据
    cell.status = self.statuses[indexPath.row];
    
    return cell;
}

#pragma -mark 代理方法
/*
    想要计算cell的高度,必须拿到cell中最后一个子控件的Y值,加上间距,就可以算出cell的高度
    想拿到最后一个子控件就必须拿到cell.通过cell.拿到cell最后一个子控件的Y值+间距.就求出cell的高度了
 */
// 在这个方法返回之前就要计算cell的高度.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{

    // 方式三: 通过数据模型来计算cell中子控件的高度
    // 通过模型为纽带,把所有子控件的frame,保存起来.在layoutsubviews中使用
    
    // 由于此代理方法调用频繁.每调用一次这个方法都要计算一次子控件的frame,性能不好.
    ZYStatus *status = self.statuses[indexPath.row];
        
    return status.cellHeight;
}


@end



根据文字来创建自适应大小方式。

  // 自适应文字及高度显示
    CGFloat textX = iconX;
    CGFloat textY = CGRectGetMaxY(iconImageView) + margin;
// 设置文字的自适应宽度 ,屏幕的宽度减去两边的间距
    CGFloat textW = [UIScreen mainScreen].bounds.size.width - iconX * 2;

// 设置文字显示的最大高度
    CGSize textMaxSize = CGSizeMake(textW, MAXFLOAT);
// 字典包装文字本身大小
    NSDictionary *textSize = @{NSFontAttributeName:[UIFont systemFontOfSize:16.0]
                               };
//  算出文字的自适应高度
    CGFloat textH = [status.text boundingRectWithSize:textMaxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:textSize context:nil].size.height;

     CGRect TEXTLabel  = CGRectMake(textX, textY, textW, textH);


// 自适应文字宽度显示
    CGFloat nameX = CGRectGetMaxX(self.iconImageView.frame) + margin;
    CGFloat nameY = iconY;
 // 计算文字本身所占据的尺寸
    NSDictionary *nameAttri = @{NSFontAttributeName :[UIFont systemFontOfSize:14.0]
                               };
    CGSize nameSize = [self.status.name sizeWithAttributes:nameAttri];
    // 包装成结构体
    self.nameLabel.frame = (CGRect){{nameX,nameY},nameSize};



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

white camel

感谢支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值