iOS 自定义tableView Cell、高度自适应

2016-09-13  本文已影响3542人  WeiHing

1.xib方式创建

每个cell的显示的内容都是固定的,也就是cell的高度都是相同的

加载数据

有plist文件数据结构如下


创建数据模型

Product.h
@interface Product : NSObject

@property (nonatomic , copy) NSString *name;
@property (nonatomic , copy) NSString *location;
@property (nonatomic , copy) NSString *count;
@property (nonatomic , copy) NSString *price;
@property (nonatomic , copy) NSString *icon;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)goodsWithDict:(NSDictionary *)dict;
@end

Product.m
@implementation Product
- (instancetype)initWithDict:(NSDictionary *)dict{
    if (self = [super init]) {
        self.name = dict[@"name"];
        self.location = dict[@"location"];
        self.count = dict[@"minproduct"];
        self.price = dict[@"price"];
        self.icon = dict[@"icon"];
//        [self setValuesForKeysWithDictionary:dict];
    }
    return self;
}
+ (instancetype)goodsWithDict:(NSDictionary *)dict{
    return [[self alloc]initWithDict:dict];
}
@end

懒加载数据

PageFirstTableViewController.m
//用来存储所有团购商品的数据
@property (nonatomic , strong) NSMutableArray *goods;

#pragma mark -lazyload
- (NSMutableArray *)goods{
    if (_goods ==nil) {
        NSString *path = [[NSBundle mainBundle]pathForResource:@"dataSource.plist" ofType:nil];
        NSArray *arrayDict = [NSArray arrayWithContentsOfFile:path];
       //字典转模型
        NSMutableArray *arrayModels = [NSMutableArray array];
        for (NSDictionary *dict in arrayDict) {
            Product *model = [Product goodsWithDict:dict];
            [arrayModels addObject:model];
        }
        _goods = arrayModels;
    }
    return _goods;
}

实现数据源协议

通过xib方式实现自定义cell

创建以一个.xib文件。在xib中拖一个UITableViewCell,设置高宽。向UITableViewCell中拖子控件。


创建一个继承自UITableViewCell的类ProductCell与xib文件的cell相关联。通过拖线的方式将cell的子控件拖线到ProductCell的属性上。

ProductCell.m
@property (weak, nonatomic) IBOutlet UILabel *name;
@property (weak, nonatomic) IBOutlet UILabel *price;
@property (weak, nonatomic) IBOutlet UIImageView *icon;
....略

实现数据源协议

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    //1.获取模型数据
    Product *model = self.goods[indexPath.row];
    //2.创建单元格
    //通过xib创建单元格
    //由于此方法调用十分频繁,cell的标示声明成静态变量有利于性能优化
    static NSString *ID = @"goods_cell"; //要在xib中设置这个id
    ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        //加载xib文件,loadNibName:方法返回的是一个数组,因为xib中可能有不止一个控件
        cell = [[[NSBundle mainBundle]loadNibNamed:@"ProductCell" owner:nil options:nil] firstObject];//不能加xib后缀
    }
    //3.把模型数据设置给单元格
    cell.name.text = model.name;
    cell.price.text = [NSString stringWithFormat:@"¥%@", model.price];
    cell.icon.image = [UIImage imageNamed: model.icon];
   ...赋值,略
    //4.返回单元格
    return cell;
}

在控制器中直接为cell重的每个子控件赋值数据造成的问题:
1.控制器强依赖于cell,一旦cell内部子控件发生了变化,那么控制器中的代码也得改(紧耦合)。控制器完全依赖于单元格里面的属性。
2.cell的封装不够完整,凡是用到cell的地方,每次都要编写为cell的子控件依次赋值的语句,比如:cell.xxx = model.xxx。如果有10个控制器,每个控制器里都需要用到单元格进行赋值,如果一个单元格里有10个子控件,那么上面这样的代码就要写10次。

对自定义cell进行封装,把模型数据设置给单元格,形如:cell.goods = model;由cell对象内部自己来解析模型数据,并把数据设置到对应的子控件中。在cell中创建一个模型类型的属性,重写该属性的set方法,在set方法中将数据赋值给控件。

ProductCell.h
#import "Product.h"
@interface ProductCell : UITableViewCell
@property (nonatomic , strong) Product *goods;
//封装一个创建自定义cell的方法
+ (instancetype)productCellWithTableView:(UITableView *)tableView;
@end

ProductCell.m
//重写set方法
- (void)setGoods:(Product *)goods{
    _goods =goods;
    self.name.text = goods.name;
    self.price.text = [NSString stringWithFormat:@"¥%@",goods.price];
    self.icon.image = [UIImage imageNamed:goods.icon];
    self.location.text = goods.location;
    self.count.text = [NSString stringWithFormat:@"最低批发量:%@",goods.count];
}
+ (instancetype)productCellWithTableView:(UITableView *)tableView{
    static NSString *ID = @"goods_cell";
    ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[[NSBundle mainBundle]loadNibNamed:@"ProductCell" owner:nil options:nil] firstObject];//不能加xib后缀
    }
    return cell;
}

修改后的数据源方法

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    Product *model = self.goods[indexPath.row];
    ProductCell *cell = [ProductCell productCellWithTableView:tableView];
    cell.goods = model;
    return cell;
}

注意:要设置tableView.rowHeight = xib中的cell的高度。不然会报警告

2.纯代码方式创建(frameLayout,自适应高度)

每个cell显示的内容不固定,cell的高度需要根据内容的多少自适应高度(例如微博,朋友圈):
iOS开发UI篇—使用纯代码自定义UItableviewcell实现一个简单的微博界面布局

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

原文例子里自定义cell自适应高度是通过加减乘除运算来计算出来的,没有使用autolayout

结合链接原文里面的例子,讲一下自己的个人理解。

另外,如果是获取单行文本的size :可以用sizeWithAttributes:方法

更新:使用xib创建自适应高度的tableViewCell
UITableViewCell高度自适应探索这篇文章写得挺细致的,没有其他要补充,大致上和用代码创建自适应高度cell的实现原理上是一样的。比起使用frameLayout创建cell要简单一点,不需要另外设置frame模型来存放所有子控件的frame、cell的高度。

现在有个第三方框架可以很方便地创建高度自适应的tableView cell:
优化UITableViewCell高度计算的那些事
UITableView+FDTemplateLayoutCell 源码阅读

上一篇下一篇

猜你喜欢

热点阅读