AutoLayout实战:cell高度不固定的UITableView
2016-12-29 09:26
316 查看
在没有AutoLayout之前,自定义一个高度不固定的cell是相当麻烦的。你需要写非常多计算尺寸的代码,在拿到数据后,需要计算cell里面每一个控件的尺寸才能最终确定cell的高度。如果你已经受够了各种计算尺寸的代码。那么本篇文章或许会对你有一些帮助,本文会说明如何利用AutoLayout优雅的实现具有动态高度的cell
高度随着内容变化,内容越多,高度就越高
内容label,就是显示
customImageView用于显示头像
title用于显示昵称
subtitle用于显示内容
系统的那套太繁琐了.
设置
重点1:告诉
重点2:注意力放到
调用
参数
如果你的项目最低支持iOS7,那么你可以在
实现该方法后,tableview就不会一次性调用完所有cell的高度,有些不在可见范围的cell是不需要一开始就知道高度的。当然,
Done!
在开始之前,先看下效果图,知道将要完成神马东西。
高度随着内容变化,内容越多,高度就越高
内容label,就是显示
天气真好的label,最多显示3行
自定义Cell,继承自UITableViewCell
1 2 3 4 5 6 7 | @interface GJCell : UITableViewCell @property (nonatomic, weak) UIImageView *customImageView; @property (nonatomic, weak) UILabel *title; @property (nonatomic, weak) UILabel *subtitle; @end |
title用于显示昵称
subtitle用于显示内容
创建UITableView并准备数据
1 2 3 4 5 6 78 | #import "GJCell.h" #import "Masonry.h" static NSString * const GJCellIndentifier = @"GJCell"; @interface ViewController () <UITableViewDataSource, UITableViewDelegate> @property (nonatomic, strong) NSArray *datas; @property (nonatomic, weak) UITableView *tableView; @end @implementation ViewController #pragma mark - Life Cycle - (void)viewDidLoad { [super viewDidLoad]; [self setupData]; [self setupView]; } - (void)setupData { NSDictionary *data1 = @{@"icon": @"myIcon", @"name": @"GJBlog", @"content": @"今天天气真好啊"}; NSDictionary *data2 = @{@"icon": @"myIcon", @"name": @"GJBlogGJBlogGJBlog", @"content": @"今天天气真好啊今天天气真好啊今天天气真好啊今天天气真好啊"}; NSDictionary *data3 = @{@"icon": @"myIcon", @"name": @"GJBlogGJBlogGJBlogGJBlogGJBlog", @"content": @"今天天气真好啊今天天气真好啊今天天气真好啊今天天气真好啊今天天气真好啊今天天气真好啊今天天气真好啊"}; self.datas = @[data1, data2, data3]; } - (void)setupView { UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; [tableView registerClass:[GJCell class] forCellReuseIdentifier:GJCellIndentifier]; tableView.dataSource = self; tableView.delegate = self; [self.view addSubview:tableView]; self.tableView = tableView; [tableView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.view); }]; } |
AutoLayout代码采用Masonry,
系统的那套太繁琐了.
tableView的大小等于
self.view的大小.
self.datas里面有三条数据,每条数据的
name和
content长度都是不一样的.尽量模拟真实情况.
icon的值是一张本地图片的名称.
实现GJCell.m
1 2 3 4 5 6 78 | #import "GJCell.h" #import "Masonry.h" @implementation GJCell - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { [self setupView]; [self setupConstraint]; } return self; } - (void)setupView { UIImageView *customImageView = [[UIImageView alloc] init]; customImageView.layer.cornerRadius = 15.0f; customImageView.layer.masksToBounds = YES; [self.contentView addSubview:customImageView]; _customImageView = customImageView; // 重点1 CGFloat preferredWidth = [UIScreen mainScreen].bounds.size.width - 75; UILabel *title = [[UILabel alloc] init]; title.numberOfLines = 0; // 重点1 title.preferredMaxLayoutWidth = preferredWidth; title.textColor = [UIColor grayColor]; [self.contentView addSubview:title]; _title = title; UILabel *subtitle = [[UILabel alloc] init]; subtitle.numberOfLines = 3; // 重点1 subtitle.preferredMaxLayoutWidth = preferredWidth; [self.contentView addSubview:subtitle]; _subtitle = subtitle; } - (void)setupConstraint { [self.customImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.contentView).with.offset(15.0f); make.right.equalTo(self.title.mas_left).with.offset(-15.0f); make.left.equalTo(self.contentView).with.offset(15.0f); make.size.mas_equalTo(CGSizeMake(30.0f, 30.0f)); }]; [self.title mas_makeConstraints:^(MASConstraintMaker *make) { // 重点2 make.top.equalTo(self.contentView).with.offset(20.0f).with.priority(751); make.right.equalTo(self.contentView).with.offset(-15.0f); }]; [self.subtitle mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.title.mas_bottom).with.offset(15.0f); make.right.equalTo(self.title); make.bottom.equalTo(self.contentView).with.offset(-15.0f).with.priority(749); make.left.equalTo(self.title); }]; } @end |
1.在setupView
方法里面添加自定义控件
设置customImageView为圆角,
title的行数不限制,
subtitle的行数最多为3行.
2.在setupConstraint
方法里面添加自定义控件的约束
customImageView顶部、左边距离父视图
15.0f,
customImageView的右边与
title的左边相距
15.0f,
customImageView的高度和宽度都等于
30.0f.
title的顶部距离父视图
20.0f,
title的右边距离父视图
15.0f.
subtitle的顶部与
title的底部相距
15.0f,
subtitle的左边等于
title的左边,
subtitle的右边等于
title的右边,
subtitle的底部距离父视图
15.0f.
3.说下代码中的两个重点
重点1:告诉AutoLayout系统``label的最大宽度,便于计算高度。如果实在无法理解,等会实现
tableview的
数据源和
代理之后,可以尝试注释掉
重点1处的代码,然后看下效果。
重点2:注意力放到
with.priority(751)这里,两个
label相邻,
label的高度都是暂时无法确定,需要告诉
AutoLayout系统约束的优先级,遇到无法同时满足约束时的优先满足级别。如果实在无法理解,同重点1,代码完善后,删掉
with.priority(751),观察下控制台的输出。
在控制器中实现UItableViewDataSource
1 2 3 4 5 6 78 | #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 60; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { GJCell *cell = [tableView dequeueReusableCellWithIdentifier:GJCellIndentifier forIndexPath:indexPath]; [self configureCell:cell atIndexPath:indexPath]; return cell; } - (void)configureCell:(GJCell *)cell atIndexPath:(NSIndexPath *)indexPath { NSInteger row = indexPath.row % 3; NSDictionary *data = self.datas[row]; UIImage *image = [UIImage imageNamed:data[@"icon"]]; [cell.customImageView setImage:image]; [cell.title setText:data[@"name"]]; [cell.subtitle setText:data[@"content"]]; } |
实现UITableViewDelegate
1 2 3 4 5 6 78 | #pragma mark - UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return [self heightForCellAtIndexPath:indexPath]; } - (CGFloat)heightForCellAtIndexPath:(NSIndexPath *)indexPath { static GJCell *cell = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ cell = [self.tableView dequeueReusableCellWithIdentifier:GJCellIndentifier]; }); [self configureCell:cell atIndexPath:indexPath]; return [self calculateHeightForCell:cell]; } - (CGFloat)calculateHeightForCell:(GJCell *)cell { [cell setNeedsLayout]; [cell layoutIfNeeded]; CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; return size.height + 1.0f; } |
方法说明
heightForCellAtIndexPath:拿出一个cell用于计算,使用
dispatch_once保证只执行一次,方法
configureCell:atIndexPath:在数据源那块已经实现了,直接调用即可。
调用
calculateHeightForCell:计算cell的高度,先调用
setNeedsLayout和
layoutIfNeeded让cell去布局子视图,然后调用
systemLayoutSizeFittingSize:让
AutoLayout系统去计算大小,
参数
UILayoutFittingCompressedSize的意思是告诉
AutoLayout系统使用尽可能小的尺寸以满足约束,返回的结果里
+1.0f是分割线的高度。
OK, 到这里基本结束了,可以编译运行了。
小小的优化一下
如果你的项目最低支持iOS7,那么你可以在UITableViewDelegate那块添加如下方法:
1 2 3 4 5 | #pragma mark - UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { return 112.0f; } |
estimatedHeightForRowAtIndexPath方法调用频率就会非常高,所以我们尽量返回一个比较接近实际结果的固定值以提高性能.
Done!
相关文章推荐
- IOS 6.0+ Autolayout — UITableViewCell 高度调整
- Autolayout uitableviewcell 自适应cell高度
- UITableViewcell autolayout下动态高度
- 结合AutoLayout实践iOS8上UITableViewCell高度的自适应
- IOS 6.0+ Autolayout — UITableViewCell 高度调整
- UITableViewcell autolayout下动态高度
- UITableViewCell使用AutoLayout自适应高度
- iOS UI设计: 在Autolayout自适应的情况下tableviewcell高度自适应
- UITableViewCell AutoLayout 动态行高
- 【iOS】UITableViewCell高度计算(固定高度+自动高度)
- 关于Xib使用AutoLayout动态设置cell高度
- iOS中手写UITableViewCell的实现与逻辑(cell固定高度展现)
- iOS autolayout自适应cell高度时使用estimatedRowHeight的一些问题
- IOS tableView cell动态高度 (autoLayout)
- UITableView介绍 之 AutoLayout下复杂cell的高度计算
- UITableViewCell高度自适应探索--UITableView+FDTemplateLayoutCell(转载)
- UITableView-FDTemplateLayoutCell----UITableViewCell高度计算的那些事
- UITableViewCell使用Autolayout布局的解决过程
- iOS7上 使用autolayout让Cell自动调整高度
- Swift-AutoLayout system UITableViewCell