UITableView的那些事儿

    本文的背景是,在实际项目中,我们经常会遇到TabelView的cell高度需要根据内容自适应,但cell的高度不是应该在cell显示前就已经设置好了吗?如何才能根据内容自适应高度?为此我们一探TabelView运行的机制.

    下面给出本例的日志

2016-01-28 10:15:18.981 SimpleImChat[1370:40265] viewDidLoad
2016-01-28 10:15:18.982 SimpleImChat[1370:40265] viewWillAppear
2016-01-28 10:15:18.993 SimpleImChat[1370:40265] -[ChatViewController tableView:heightForRowAtIndexPath:]:0
2016-01-28 10:15:18.994 SimpleImChat[1370:40265] -[ChatViewController tableView:heightForRowAtIndexPath:]:0
2016-01-28 10:15:18.997 SimpleImChat[1370:40265] viewWillLayoutSubviews
2016-01-28 10:15:18.999 SimpleImChat[1370:40265] -[ChatViewController tableView:heightForRowAtIndexPath:]:0
2016-01-28 10:15:18.999 SimpleImChat[1370:40265] viewDidLayoutSubviews
2016-01-28 10:15:19.000 SimpleImChat[1370:40265] -[ChatViewController tableView:heightForRowAtIndexPath:]:0
2016-01-28 10:15:19.001 SimpleImChat[1370:40265] -[ChatViewController tableView:cellForRowAtIndexPath:]:0
2016-01-28 10:15:19.056 SimpleImChat[1370:40265] -[ChatViewController tableView:heightForRowAtIndexPath:]:0
2016-01-28 10:15:19.058 SimpleImChat[1370:40265] viewWillLayoutSubviews
2016-01-28 10:15:19.059 SimpleImChat[1370:40265] viewDidLayoutSubviews
2016-01-28 10:15:19.064 SimpleImChat[1370:40265] viewDidAppear

可以看到heightForRowAtIndexPath执行多次,而且是先于cellForRowAtIndexPath执行的,那么我们是不是应该在heightForRowAtIndexPath里面提前设置好cell的高度,然后cellForRowAtIndexPath执行的时候就已经可以正确的显示了呢?那么如何才能正确获取到cell的高度呢,cellForRowAtIndexPath执行前应该还没有数据的,有人会想到在heightForRowAtIndexPath里面先给cell设数据,然后算好高度再取出来,对于IOS6及以下的版本,这却是一种解决方案.但对于IOS7及以上的版本,这样做将大大降低执行效率,因为heightForRowAtIndexPath会执行多次,每次都去计算高度将很耗时.So,IOS7有了这样的代理和属性

// Use the estimatedHeight methods to quickly calcuate guessed values which will allow for fast load times of the table.
// If these methods are implemented, the above -tableView:heightForXXX calls will be deferred until views are ready to be displayed, so more expensive logic can be placed there.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(7_0);

@property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // default is 0, which means there is no estimate

这里是IOS的UITableView.h文件中的声明,这里要注意里面的注释,根据注释,我们知道,如果设置了estimatedRowHeight或者实现了estimatedHeightForRowAtIndexPath代理,就不会频繁调用heightForRowAtIndexPath了,然并卵,看LOG

2016-01-28 10:26:30.175 SimpleImChat[1462:49281] viewDidLoad
2016-01-28 10:26:30.176 SimpleImChat[1462:49281] viewWillAppear
2016-01-28 10:26:30.189 SimpleImChat[1462:49281] -[ChatViewController tableView:estimatedHeightForRowAtIndexPath:]:0
2016-01-28 10:26:30.190 SimpleImChat[1462:49281] -[ChatViewController tableView:estimatedHeightForRowAtIndexPath:]:0
2016-01-28 10:26:30.194 SimpleImChat[1462:49281] viewWillLayoutSubviews
2016-01-28 10:26:30.195 SimpleImChat[1462:49281] -[ChatViewController tableView:estimatedHeightForRowAtIndexPath:]:0
2016-01-28 10:26:30.196 SimpleImChat[1462:49281] viewDidLayoutSubviews
2016-01-28 10:26:30.198 SimpleImChat[1462:49281] -[ChatViewController tableView:estimatedHeightForRowAtIndexPath:]:0
2016-01-28 10:26:30.198 SimpleImChat[1462:49281] -[ChatViewController tableView:cellForRowAtIndexPath:]:0
2016-01-28 10:26:30.254 SimpleImChat[1462:49281] -[ChatViewController tableView:heightForRowAtIndexPath:]:0
2016-01-28 10:26:30.257 SimpleImChat[1462:49281] viewWillLayoutSubviews
2016-01-28 10:26:30.257 SimpleImChat[1462:49281] viewDidLayoutSubviews
2016-01-28 10:26:30.261 SimpleImChat[1462:49281] viewDidAppear

这里我们可以清楚的看到,当实现了estimatedHeightForRowAtIndexPath代理后,是先执行cellForRowAtIndexPath后执行

heightForRowAtIndexPath,so,你可以在cellForRowAtIndexPath中算好高度然后存下来,存到哪里?存到View?存到model?其实都可以,但cell有重用,为防止取错cell(如何才是正确的取法?读者可以思考一下),我们将算好的高度存在model里,这里你可以给model加一个属性,或者动态绑定一个属性,这样在ViewController中,我们是能够拿到所有数据的,然后根据indexPath是可以正确取到当前cell对应的model的,这里的model已经在cellForRowAtIndexPath执行的时候通过给view赋值算好了高度,并且存了下来.说了这么多,可能有点凌乱了.

这里附上代码 git代码

需要说明一下如果在viewDidLoad中设置了estimatedRowHeight就不用实现对应的代理了,而且一般情况这个预估高度都是定值,但是不能随便设置,不能随便设置,不能随便设置,这里大了小了都会有显示的问题,读者可以尝试一下,最好的就是设置一个平均值.

2016-01-28 10:38:22.070 SimpleImChat[1524:58078] viewDidLoad
2016-01-28 10:38:22.071 SimpleImChat[1524:58078] viewWillAppear
2016-01-28 10:38:22.085 SimpleImChat[1524:58078] viewWillLayoutSubviews
2016-01-28 10:38:22.086 SimpleImChat[1524:58078] viewDidLayoutSubviews
2016-01-28 10:38:22.087 SimpleImChat[1524:58078] -[ChatViewController tableView:cellForRowAtIndexPath:]:0
2016-01-28 10:38:22.145 SimpleImChat[1524:58078] -[ChatViewController tableView:heightForRowAtIndexPath:]:0
2016-01-28 10:38:22.147 SimpleImChat[1524:58078] viewWillLayoutSubviews
2016-01-28 10:38:22.147 SimpleImChat[1524:58078] viewDidLayoutSubviews
2016-01-28 10:38:22.151 SimpleImChat[1524:58078] viewDidAppear

上面是设置了预估高度,并且不实现相关代理的LOG,所以,我们看到这里的执行是很高效的,即使你频繁reload也影响不大,因为model里已经缓存了对应的cell高度.


你可能感兴趣的:(UITableView,高效执行)