自定义非等高 Cell

Posted ch520

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义非等高 Cell相关的知识,希望对你有一定的参考价值。

1、自定义非等高 Cell介绍

  • 1.1 代码自定义(frame)

    • 新建一个继承自 UITableViewCell 的类。
    • 重写 initWithStyle:reuseIdentifier: 方法。
      • 添加所有需要显示的子控件(不需要设置子控件的数据和 frame, 子控件要添加到 contentView 中)。
      • 进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体固定的图片)。
    • 提供 2 个模型。
      • 数据模型: 存放文字数据图片数据。
      • frame 模型: 存放数据模型所有子控件的 framecell 的高度。
    • cell 拥有一个 frame 模型(不要直接拥有数据模型)。
    • 重写 cell frame 模型属性的 setter 方法: 在这个方法中设置子控件的显示数据和 frame。
    • frame 模型数据的初始化已经采取懒加载的方式(每一个 cell 对应的 frame 模型数据只加载一次)。
  • 1.2 代码自定义(Autolayout)

    • 新建一个继承自 UITableViewCell 的类。
    • 重写 initWithStyle:reuseIdentifier: 方法。
      • 添加所有需要显示的子控件(不需要设置子控件的数据和 frame, 子控件要添加到 contentView 中)。
      • 进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体固定的图片)。
    • 设置 cell 上子控件的约束。
    • 在模型中增加一个 cellHeight 属性,用来存放对应 cell 的高度。
    • 在 cell 的模型属性 set 方法中调用 [self layoutIfNeed] 方法强制布局,然后计算出模型的 cellheight 属性值。
    • 在控制器中实现 tableView:estimatedHeightForRowAtIndexPath: 方法,返回一个估计高度,比如 200。
    • 在控制器中实现 tableView:heightForRowAtIndexPath: 方法,返回 cell 的真实高度(模型中的 cellHeight 属性)。

2、代码

  • 2.1 XMGStatus.h

@interface XMGStatus : NSObject

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *text;
@property (strong, nonatomic) NSString *icon;
@property (strong, nonatomic) NSString *picture;
@property (assign, nonatomic, getter=isVip) BOOL vip;

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

+ (instancetype)statusWithDict:(NSDictionary *)dict;

@end
  • 2.2 XMGStatus.m

@implementation XMGStatus

+ (instancetype)statusWithDict:(NSDictionary *)dict {

    XMGStatus *status = [[self alloc] init];
    [status setValuesForKeysWithDictionary:dict];
    return status;
}

@end
  • 2.3 XMGStatusCell.h

@class XMGStatus;

@interface XMGStatusCell : UITableViewCell

+ (instancetype)cellWithTableView:(UITableView *)tableView;

/** 模型数据 */
@property (nonatomic, strong) XMGStatus *status;

@end
  • 2.4 XMGStatusCell.m

#define MAS_SHORTHAND
#define MAS_SHORTHAND_GLOBALS

#import "Masonry.h"

@interface XMGStatusCell()

@property (weak, nonatomic) UIImageView *iconView;
@property (weak, nonatomic) UILabel *nameLabel;
@property (weak, nonatomic) UIImageView *vipView;
@property (weak, nonatomic) UILabel *contentLabel;
@property (weak, nonatomic) UIImageView *pictureView;

@end

@implementation XMGStatusCell

+ (instancetype)cellWithTableView:(UITableView *)tableView {

    static NSString *ID = @"status";
    XMGStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    if (cell == nil) {
        cell = [[XMGStatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
    }
    return cell;
}

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {

    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {

        UIImageView *iconView = [[UIImageView alloc] init];
        [self.contentView addSubview:iconView];
        self.iconView = iconView;

        UILabel *nameLabel = [[UILabel alloc] init];
        [self.contentView addSubview:nameLabel];
        self.nameLabel = nameLabel;

        UIImageView *vipView = [[UIImageView alloc] init];
        [self.contentView addSubview:vipView];
        self.vipView = vipView;

        UILabel *contentLabel = [[UILabel alloc] init];
        contentLabel.numberOfLines = 0;

        // 设置 label 每一行文字的最大宽度
        contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;

        [self.contentView addSubview:contentLabel];
        self.contentLabel = contentLabel;

        UIImageView *pictureView = [[UIImageView alloc] init];
        [self.contentView addSubview:pictureView];
        self.pictureView = pictureView;
    }
    return self;
}

- (void)layoutSubviews {

    [super layoutSubviews];
    CGFloat margin = 10;

    [self.iconView makeConstraints:^(MASConstraintMaker *make) {
        make.size.equalTo(30);
        make.left.top.offset(margin);
    }];

    [self.nameLabel makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.iconView);
        make.left.equalTo(self.iconView.right).offset(margin);
    }];

    [self.vipView makeConstraints:^(MASConstraintMaker *make) {
        make.size.equalTo(14);
        make.left.equalTo(self.nameLabel.right).offset(margin);
        make.centerY.equalTo(self.nameLabel.centerY);
    }];

    [self.contentLabel makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.iconView.bottom).offset(margin);
        make.left.offset(margin);
        //        make.right.offset(-margin);       // 可加可不加
    }];

    [self.pictureView makeConstraints:^(MASConstraintMaker *make) {
        make.size.equalTo(100);
        make.top.equalTo(self.contentLabel.bottom).offset(margin);
        make.left.offset(margin);
    }];
}

- (void)setStatus:(XMGStatus *)status {
    _status = status;

    // 设置显示的数据

    self.iconView.image = [UIImage imageNamed:status.icon];
    self.nameLabel.text = status.name;

    if (status.isVip) {
        self.nameLabel.textColor = [UIColor orangeColor];
        self.vipView.hidden = NO;
    } else {
        self.nameLabel.textColor = [UIColor blackColor];
        self.vipView.hidden = YES;
    }

    self.contentLabel.text = status.text;

    if (status.picture) {
        self.pictureView.hidden = NO;
        self.pictureView.image = [UIImage imageNamed:status.picture];
    } else {
        self.pictureView.hidden = YES;
    }

    // 计算 cell 高度

    // 强制布局
    [self layoutIfNeeded];

    // 计算 cell 的高度
    if (self.pictureView.hidden) {  // 没有配图
        _status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10;
    } else {                        // 有配图
        _status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10;
    }
}

@end
  • 2.5 XMGStatusesViewController.m

@interface XMGStatusesViewController ()

@property (strong, nonatomic) NSArray *statuses;

@end

@implementation XMGStatusesViewController

- (NSArray *)statuses {

    if (_statuses == nil) {

        // 加载plist中的字典数组
        NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil];
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];

        // 字典数组 -> 模型数组
        NSMutableArray *statusArray = [NSMutableArray array];
        for (NSDictionary *dict in dictArray) {
            XMGStatus *status = [XMGStatus statusWithDict:dict];
            [statusArray addObject:status];
        }
        _statuses = statusArray;
    }
    return _statuses;
}

#pragma mark - Table view data source

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    XMGStatusCell *cell = [XMGStatusCell cellWithTableView:tableView];
    cell.status = self.statuses[indexPath.row];
    return cell;
}

#pragma mark - 代理方法
// 返回每一行的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    XMGStatus *staus = self.statuses[indexPath.row];
    return staus.cellHeight;
}

/**
* 返回每一行的估计高度
* 只要返回了估计高度,那么就会先调用 tableView:cellForRowAtIndexPath: 方法创建 cell,
* 再调用 tableView:heightForRowAtIndexPath: 方法获取 cell 的真实高度
*/
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {

    return 200;
}

@end
  • 2.6 运行效果图

  • 技术分享图片 --- 技术分享图片

3、其它设置方式

  • 3.1 计算方式

    • BookModel.h
    @property(nonatomic, copy)NSString *title;
    @property(nonatomic, copy)NSString *detail;
    @property(nonatomic, copy)NSString *icon;
    @property(nonatomic, copy)NSString *price;
    • BookCell.h
    @property(nonatomic, retain)UILabel *titleLabel;
    @property(nonatomic, retain)UILabel *detailLabel;
    @property(nonatomic, retain)UIImageView *iconView;
    @property(nonatomic, retain)UILabel *priceLabel;
    • 设置行高
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        // 从数据源数组中取出数据
        BookModel *bookModel = [myDataArray objectAtIndex:indexPath.row];
    
        // 计算 detailLabel 占用的高度
        CGFloat detialHeight = [bookModel.detail boundingRectWithSize:CGSizeMake(self.view.bounds.size.width - 40, CGFLOAT_MAX) 
        options:NSStringDrawingUsesLineFragmentOrigin 
        attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14]} 
        context:nil].size.height;
    
        // 判断是否有图片
        if (bookModel.icon.length) {
    
            // 60 为图片的高度
            return 30 + detialHeight + 60 + 30;
        }
        else {
            return 30 + detialHeight + 30;
        }
    }
    • 设置每一行显示的内容
    // 设置每一行显示的内容
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        BookCell3 *cell = [tableView dequeueReusableCellWithIdentifier:@"test" forIndexPath:indexPath];
        BookModel *bookModel = [myDataArray objectAtIndex:indexPath.row];
    
        // 设置 titleLabel
        cell.titleLabel.text = bookModel.title;
    
        // 设置 detailLabel
    
        // 计算 detailLabel 的高度
        CGSize detialSize = [bookModel.detail boundingRectWithSize:CGSizeMake(self.view.bounds.size.width - 40, CGFLOAT_MAX) 
        options:NSStringDrawingUsesLineFragmentOrigin 
        attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14]} 
        context:nil].size;
    
        CGRect detialFrame = cell.detailLabel.frame;
        detialFrame.size.height = detialSize.height + 5;    // 加偏移量 5,适应标点无法换行
        detialFrame.size.width = detialSize.width + 5;
        cell.detailLabel.frame = detialFrame;               // 设置 detailLabel 的 frame
    
        cell.detailLabel.text = bookModel.detail;
    
        // 判断是否有图片
        if (bookModel.icon.length) {
    
            // 设置 iconView
    
            CGRect iconFrame = cell.iconView.frame;
            iconFrame.origin.y = detialFrame.origin.y + detialFrame.size.height;
            cell.iconView.frame = iconFrame;
    
            cell.iconView.image = [UIImage imageNamed: bookModel.icon];
    
            // 设置 priceLabel
    
            CGRect priceFrame = cell.priceLabel.frame;
            priceFrame.origin.y = iconFrame.origin.y + iconFrame.size.height;
            cell.priceLabel.frame = priceFrame;
    
            cell.priceLabel.text = bookModel.price;
        }
        else {
    
            // 设置 priceLabel
    
            CGRect priceFrame = cell.priceLabel.frame;
            priceFrame.origin.y = detialFrame.origin.y + detialFrame.size.height;
            cell.priceLabel.frame = priceFrame;
    
            cell.priceLabel.text = bookModel.price;
        }
        return cell;
    }
  • 3.2 系统自动布局方式

    • 自适应 cell 中较高的一个视图的高度。
    • ImageView 与 Label 同行显示,且都设置了上下边缘约束,ImageView 的图片填充模式为 Aspect Fit,否则图片将会被拉长。
    • 协议方法 方式设置
      ``` Objective-C
      // 动态设置行高
    • (CGFloat)tableView:(UITableView )tableView estimatedHeightForRowAtIndexPath:(NSIndexPath )indexPath {

      /
      行高自适应 Label 高度
      /

      secondTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"secondTableViewCell" forIndexPath:indexPath];

      cell.secondLabel.text = [_labelArray objectAtIndex:indexPath.row];

      return [cell.contentView systemLayoutSizeFittingSize:(UILayoutFittingCompressedSize)].height + 1;
      }

    // 属性变量 方式设置
    self.tableView.estimatedRowHeight = 80;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    ```技术分享图片

以上是关于自定义非等高 Cell的主要内容,如果未能解决你的问题,请参考以下文章

源码-0203-06-自定义非等高cell-xib

iOS边练边学--自定义非等高的cell

源码-0203-06-自定义等高cell05-代码-Autolayout

自定义等高 Cell

div+css:两个div并排等高 (table-cell)

IOS 通过 代码 自定义cell(Cell的高度不一致)(优化性能)