UITableView最核心的思想就是UITableViewCell的重用机制。
UITableView的布局
UITableView最主要的两个回调方法是tableView:cellForRowAtIndexPath:和tableView:heightForRowAtIndexPath:。理想上我们是会认为UITableView会先调用前者,再调用后者,因为这和我们创建控件的思路是一样的,先创建它,再设置它的布局。但实际上却并非如此,我们都知道,UITableView是继承自UIScrollView的,需要先确定它的contentSize及每个Cell的位置,然后才会把重用的Cell放置到对应的位置。所以事实上,UITableView的回调顺序是先多次调用tableView:heightForRowAtIndexPath:以确定contentSize及Cell的位置,然后才会调用tableView:cellForRowAtIndexPath:,从而来显示在当前屏幕的Cell。
优化思路:
举个例子来说:如果现在要显示100个Cell,当前屏幕显示5个。那么刷新(reload)UITableView时,UITableView会先调用100次tableView:heightForRowAtIndexPath:方法,然后调用5次tableView:cellForRowAtIndexPath:方法;滚动屏幕时,每当Cell滚入屏幕,都会调用一次tableView:heightForRowAtIndexPath:、tableView:cellForRowAtIndexPath:方法。
重写drawRect方法就不需要用GCD异步线程了,因为drawRect本来就是异步绘制的。
//按需加载 - 如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。
```objc
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollViewwithVelocity:(CGPoint)velocitytargetContentOffset:(inout CGPoint *)targetContentOffset{
NSIndexPath *ip = [selfindexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];
NSIndexPath *cip = [[self indexPathsForVisibleRows] firstObject];
NSInteger skipCount =8;
if(labs(cip.row-ip.row)>skipCount) {
NSArray *temp = [selfindexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, self.width, self.height)];
NSMutableArray *arr = [NSMutableArrayarrayWithArray:temp];
if(velocity.y<0) {
NSIndexPath *indexPath = [temp lastObject];
if(indexPath.row+3
[arraddObject:[NSIndexPathindexPathForRow:indexPath.row+1inSection:0]];
[arraddObject:[NSIndexPathindexPathForRow:indexPath.row+2inSection:0]];
[arraddObject:[NSIndexPathindexPathForRow:indexPath.row+3inSection:0]];
}
}else{
NSIndexPath *indexPath = [temp firstObject];
if(indexPath.row>3) {
[arraddObject:[NSIndexPathindexPathForRow:indexPath.row-3inSection:0]];
[arraddObject:[NSIndexPathindexPathForRow:indexPath.row-2inSection:0]];
[arraddObject:[NSIndexPathindexPathForRow:indexPath.row-1inSection:0]];
}
}
[needLoadArraddObjectsFromArray:arr];
}
}
```
记得在tableView:cellForRowAtIndexPath:方法中加入判断:
```objc
if(needLoadArr.count>0&&[needLoadArr indexOfObject:indexPath]==NSNotFound) {
[cellclear];
return;
}
```
滚动很快时,只加载目标范围内的Cell,这样按需加载,极大的提高流畅度。
提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;
异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;
滑动时按需加载,这个在大量图片展示,网络加载的时候很管用!(SDWebImage已经实现异步加载,配合这条性能杠杠的)。
除了上面最主要的三个方面外,还有很多几乎大伙都很熟知的优化点:
正确使用reuseIdentifier来重用Cells
尽量使所有的viewopaque,包括Cell自身
尽量少用或不用透明图层
如果Cell内现实的内容来自web,使用异步加载,缓存请求结果
减少subviews的数量
在heightForRowAtIndexPath:中尽量不使用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后缓存结果
尽量少用addView给Cell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示
网友评论