在tableview在使用autolayout,自适应cell的高度(iOS7+)


一、概述

1、声明NSDictinary *offscreenCell

a. 用来存储每种类型cell的一个对象实例
b. 此dictionarykey为每种类型cell的reuse identifiervalue为该类型cell的一个对象实例

2、cell的注册

a. 使用tableviewregisterClass方法来进行cell的注册
b. 若有多种类型的cell,则要用registerClass注册多个cell

3、初始化cell,并返回cell高度

a. 在heightForRowAtIndexPath方法里,进行cell的初始化
b. 使用systemLayoutSizeFittingSize方法获取cell的高度

4、cell赋值

cellForRowAtIndexPath方法里,进行cell的赋值

5、使用autolayout对需要的控件进行布局

有两种方式:
a. 使用xib
b. 重写自定义cell类里的updateConstraints方法,手动用代码进行布局

注:本文使用Masonry进行布局

6、通知系统进行布局

在自定义的cell类里的layoutSubviews方法,调用相关方法通知系统进行布局

二、关键点

1、autolayout要设置正确,如果不正确,systemLayoutSizeFittingSize方法计算出来的高度是0

三、示例代码

假设有两种类型的cell,先自定义两个cell类
分别命名为AutoResizeCell SecondResizeCell
reuse identify分别为autoResizeCellId secondResizeCellId

1、在controller里的viewDidLoad

1
2
3
4
5
6
7
8
9
10
/////////////// step: 1 ///////////////
self.offscreenCell = [NSMutableDictionary dictionary];
/////////////// step: 1 ///////////////

/////////////// step: 2 ///////////////
[self.myTableView registerClass:[AutoResizeCell class] forCellReuseIdentifier:autoResizeCellId];
[self.myTableView registerClass:[SecondResizeCell class] forCellReuseIdentifier:secondResizeCellId];
// Setting the estimated row height prevents the table view from calling tableView:heightForRowAtIndexPath: for every row in the table on first load;
// it will only be called as cells are about to scroll onscreen. This is a major performance optimization.self.myTableView.estimatedRowHeight = UITableViewAutomaticDimension; // iOS7+
/////////////// step: 2 ///////////////

2、在controller里的heightForRowAtIndexPath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

/////////////// step: 3 ///////////////
NSDictionary *aDict = [self getRowDataDictInSection:indexPath.section row:indexPath.row];
BOOL hasAvatar = [[aDict objectForKey:HAS_AVATAR] boolValue];
NSString *reuseIdentifier = (hasAvatar ? secondResizeCellId : autoResizeCellId);
UITableViewCell *cell = [self.offscreenCell objectForKey:reuseIdentifier];
if (!cell) {
if (hasAvatar) {
cell = [[SecondResizeCell alloc] init];
} else {
cell = [[AutoResizeCell alloc] init];
}
[self.offscreenCell setObject:cell forKey:reuseIdentifier];
}
if (hasAvatar) {
[(SecondResizeCell*)cell initModel:aDict];
} else {
[(AutoResizeCell*)cell initModel:aDict];
}
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));
[cell setNeedsLayout];
[cell layoutIfNeeded];

CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
height += 1;
/////////////// step: 3 ///////////////

return height;
}

3、在controller里的cellForRowAtIndexPath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

/////////////// step: 4 ///////////////
NSDictionary *aDict = [self getRowDataDictInSection:indexPath.section row:indexPath.row];
BOOL hasAvatar = [[aDict objectForKey:HAS_AVATAR] boolValue];
NSString *reuseIdentifier = (hasAvatar ? secondResizeCellId : autoResizeCellId);
AutoResizeCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (hasAvatar) {
[(SecondResizeCell*)cell initModel:aDict];
} else {
[(AutoResizeCell*)cell initModel:aDict];
}
/////////////// step: 4 ///////////////
return cell;
}

4、在自定义cell类的initModel

1
2
3
4
5
6
7
8
9
10
11
- (void)initModel:(id)model {

self.titleLabel.text = [model objectForKey:@"title"];
self.bodyLabel.text = [model objectForKey:@"content"];

// Make sure the constraints have been added to this cell, since it may have just been created from scratch
/////////////// step: 4 ///////////////
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
/////////////// step: 4 ///////////////
}

5、在自定义cell类的updateConstraints

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)updateConstraints {

/////////////// step: 5 ///////////////
if (!self.didSetupConstraints) {

// titleLabel
[_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(kLabelVerticalInsets);
make.left.mas_equalTo(kLabelHorizontalInsets);
make.right.mas_equalTo(-kLabelHorizontalInsets); // need
}];
// bodyLabel
[_bodyLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(_titleLabel.mas_bottom).with.offset(kLabelVerticalInsets);
make.left.mas_equalTo(_titleLabel.mas_left);
make.bottom.mas_equalTo(-kLabelVerticalInsets); // need
make.right.mas_equalTo(_titleLabel.mas_right);
}];
self.didSetupConstraints = YES;
}
[super updateConstraints];
/////////////// step: 5 ///////////////
}

6、在自定义cell类的layoutSubviews

1
2
3
4
5
6
7
8
9
10
11
- (void)layoutSubviews {
[super layoutSubviews];

/////////////// step: 6 ///////////////
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];

_titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(_titleLabel.frame);
_bodyLabel.preferredMaxLayoutWidth = CGRectGetWidth(_bodyLabel.frame);
/////////////// step: 6 ///////////////
}

四、完整demo代码

点击跳转到github

五、autolayout相关资料

1)开始iOS 7中自动布局教程(上下部分)
1、http://www.cocoachina.com/industry/20131203/7462.html
2、http://www.cnblogs.com/zer0Black/p/3977288.html

2)Masonry说明:
官方:https://github.com/Masonry/Masonry
第三方:http://adad184.com/2014/09/28/use-masonry-to-quick-solve-autolayout/

3)tableview动态计算cell高度
1、 http://www.ifun.cc/blog/2014/02/21/dong-tai-ji-suan-uitableviewcellgao-du-xiang-jie/
2、http://www.devdiv.com/autolayout_uitableviewcell_-blog-21666-52543.html
3、http://www.tuicool.com/articles/FZN3q2
4、http://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights