原文地址:http://www.mgenware.com/blog/?p=507
首先需要在Storyboard中创建好TableViewController,使用动态Cell,在Prototype Cells中设计好Cell界面。
接着,定义好Autolayout,注意Autolayout一定要在上下都绑定控件的位置,不要只从上到下定义,只有正确定义Autolayout,后面我们用到的systemLayoutSizeFittingSize方法才会返回正确的结果。
如下图:
Xcode会提示Autolayout的各种Ambiguity,提示修改控件的Compression resistance,如下图:
这里让Xcode智能修正就可以了,具体哪个控件的Compression resistance无所谓,因为我们最终的目的是让UITabelViewCell的高度去适合所有控件的大小。
然后,因为是在Xcode 5 iOS 7模式下设计的Storyboard,所以在iOS 7下运行肯定是没问题的:
接着在iOS 6上运行:
什么情况?
出现这个问题的原因是:iOS 7和iOS 6中的许多控件默认高度都是不一样的,在其他普通UIView下,有了Autolayout,控件当然会正确显示。但是UITableViewCell的高度是通过UITableView的heightForRowAtIndexPath方法来返回的。默认情况下,它是保持不变的。所以当Cell内控件的高度发生变化后,如果Cell高度没有因此而作出调整,肯定会出问题的。
为了解决问题,我们必须在TableView的heightForRowAtIndexPath方法中返回根据Cell内容计算出来的动态高度,而如果Cell数量不是很多的话,我们可以在heightForRowAtIndexPath中把创建好的Cell缓存起来,然后在cellForRowAtIndexPath中直接使用这个缓存的Cell就可以。当然如果Cell比较多需要Cell重用逻辑的话,则需要在heightForRowAtIndexPath和cellForRowAtIndexPath都调用dequeueReusableCellWithIdentifier方法。
另外如果在heightForRowAtIndexPath调用dequeueReusableCellWithIdentifier:forIndexPath:方法的话,会出现栈溢出问题,类似这样:
也就是说dequeueReusableCellWithIdentifier:forIndexPath:会反过来调用heightForRowAtIndexPath方法。
那么我们可以使用旧的dequeueReusableCellWithIdentifier方法,也就是没有IndexPath参数的,这个是可以使用的,当然使用dequeueReusableCellWithIdentifier的话,我们需要手动判断Cell返回nil的情况。
OK开始写代码:在TableViewController中加入必要的字段:
//测试数据源 NSMutableArray *_dataSource;
在viewDidLoad中初始化相关数据:
//viewDidLoad 初始化 _dataSource = [NSMutableArray arrayWithArray:@[@"Mgen", @"Tony", @"Jerry", @"一二三"]];
把创建Cell逻辑写在一个方法内(注意在heightForRowAtIndexPath:indexPath中无法使用dequeueReusableCellWithIdentifier:forIndexPath:方法,所以这里需使用dequeueReusableCellWithIdentifier方法):
- (MyCell*)getCellFromIndexPath:(NSIndexPath*)indexPath { static NSString *CellIdentifier = @"MyCell"; //注意在heightForRowAtIndexPath:indexPath无法使用dequeueReusableCellWithIdentifier:forIndexPath: MyCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //用dequeueReusableCellWithIdentifier:就得判断Cell为nil的情况 if (!cell) { cell = [[MyCell alloc] init]; } //这里把数据设置给Cell cell.titleLabel.text = [_dataSource objectAtIndex:indexPath.row]; return cell; }
在heightForRowAtIndexPath中使用systemLayoutSizeFittingSize方法来计算创建Cell的高度并返回,如下代码:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { //获取Cell MyCell *cell = [self getCellFromIndexPath:indexPath]; //更新UIView的layout过程和Autolayout [cell setNeedsUpdateConstraints]; [cell updateConstraintsIfNeeded]; [cell.contentView setNeedsLayout]; [cell.contentView layoutIfNeeded]; //通过systemLayoutSizeFittingSize返回最低高度 CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; return height; }
接着cellForRowAtIndexPath方法:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { return [self getCellFromIndexPath:indexPath]; }
OK,现在即便是你把Cell高度手动调整成这样:
在iOS 6下会显示出正确的结果:
iOS 7下也下一样:
源代码下载
tableViewCellHeight.zip
源代码环境:Xcode 5.0