RunLoop总结:RunLoop的应用场景(三)滚动视图流畅性优化

作者:哈雷哈雷_Wong

让UITableView、UICollectionView等延迟加载图片。 下面就拿UITableView来举例说明: UITableView 的 cell 上显示网络图片,一般需要两步,第一步下载网络图片; 第二步,将网络图片设置到UIImageView上。

为了不影响滑动,第一步,我们一般都是放在子线程中来做,这个不做赘述。

第二步,一般是回到主线程去设置。 有了前两篇文章关于Mode的切换,想必你已经知道怎么做了。 就是在为图片视图设置图片时,在主线程设置,并调用

performSelector:withObject:afterDelay:inModes: 方法。最后一个参数,仅设置一个 NSDefaultRunLoopMode

UIImage *downloadedImage = ....;

[self.myImageView performSelector:@selector(setImage:) withObject:downloadedImage afterDelay:0 inModes:@[NSDefaultRunLoopMode]];

当然,即使是读取沙盒或者bundle内的图片,我们也可以运用这一点来改善视图的滑动。但是如果UITableView上的图片都是默认图,似乎也不是很好,你需要自己来权衡了。

有一个非常好的关于设置图片视图的图片,在RunLoop切换Mode时优化的例子:RunLoopWorkDistribution 先看一下界面布局:

一个Cell里有两个Label,和三个imageView,这里的图片是非常高清的(2034 × 1525),一个界面最多有18张图片。为了表现出卡顿的效果,我先自己实现了一下Cell,主要示例代码:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

static NSString *identifier = @"cellId";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

if (cell == nil) {

cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];

}

for (NSInteger i = 1; i <= 5; i++) {

[[cell.contentView viewWithTag:i] removeFromSuperview];

}

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5, 5, 300, 25)];

label.backgroundColor = [UIColor clearColor];

label.textColor = [UIColor redColor];

label.text = [NSString stringWithFormat:@"%zd - Drawing index is top priority", indexPath.row];

label.font = [UIFont boldSystemFontOfSize:13];

label.tag = 1;

[cell.contentView addSubview:label];

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(105, 20, 85, 85)];

imageView.tag = 2;

NSString *path = [[NSBundle mainBundle] pathForResource:@"spaceship" ofType:@"jpg"];

UIImage *image = [UIImage imageWithContentsOfFile:path];

imageView.contentMode = UIViewContentModeScaleAspectFit;

imageView.image = image;

NSLog(@"current:%@",[NSRunLoop currentRunLoop].currentMode);

[cell.contentView addSubview:imageView];

UIImageView *imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(200, 20, 85, 85)];

imageView2.tag = 3;

UIImage *image2 = [UIImage imageWithContentsOfFile:path];

imageView2.contentMode = UIViewContentModeScaleAspectFit;

imageView2.image = image2;

[cell.contentView addSubview:imageView2];

UILabel *label2 = [[UILabel alloc] initWithFrame:CGRectMake(5, 99, 300, 35)];

label2.lineBreakMode = NSLineBreakByWordWrapping;

label2.numberOfLines = 0;

label2.backgroundColor = [UIColor clearColor];

label2.textColor = [UIColor colorWithRed:0 green:100.f/255.f blue:0 alpha:1];

label2.text = [NSString stringWithFormat:@"%zd - Drawing large image is low priority. Should be distributed into different run loop passes.", indexPath.row];

label2.font = [UIFont boldSystemFontOfSize:13];

label2.tag = 4;

UIImageView *imageView3 = [[UIImageView alloc] initWithFrame:CGRectMake(5, 20, 85, 85)];

imageView3.tag = 5;

UIImage *image3 = [UIImage imageWithContentsOfFile:path];

imageView3.contentMode = UIViewContentModeScaleAspectFit;

imageView3.image = image3;

[cell.contentView addSubview:label2];

[cell.contentView addSubview:imageView3];


return cell;

}


然后在滑动的时候,顺便打印出当前的runloopMode,打印结果是:

2016-12-08 10:34:31.450 TestDemo[3202:1791817] current:UITrackingRunLoopMode

2016-12-08 10:34:31.701 TestDemo[3202:1791817] current:UITrackingRunLoopMode

2016-12-08 10:34:32.184 TestDemo[3202:1791817] current:UITrackingRunLoopMode

2016-12-08 10:34:36.317 TestDemo[3202:1791817] current:UITrackingRunLoopMode

2016-12-08 10:34:36.601 TestDemo[3202:1791817] current:UITrackingRunLoopMode

2016-12-08 10:34:37.217 TestDemo[3202:1791817] current:UITrackingRunLoopMode


可以看出,为imageView设置image,是在 UITrackingRunLoopMode 中进行的,如果图片很大,图片解压缩和渲染肯定会很耗时,那么卡顿就是必然的。

查看实时帧率,我们可以在Xcode 中选择 真机调试 ,然后 Product -->Profile-->Core Animation

然后点击开始监测即可:

下面就是帧率:

这里就可以使用先使用上面的方式做一次改进。

[imageView performSelector:@selector(setImage:) withObject:image afterDelay:0 inModes:@[NSDefaultRunLoopMode]];

可以保证在滑动起来顺畅,可是停下来之后,渲染还未完成时,继续滑动就会变的卡顿。在切换到 NSDefaultRunLoopMode 中,一个runloop循环要解压和渲染18张大图,耗时肯定超过50ms(1/60s)。

可以保证在滑动起来顺畅,可是停下来之后,渲染还未完成时,继续滑动就会变的卡顿。在切换到 NSDefaultRunLoopMode 中,一个runloop循环要解压和渲染18张大图,耗时肯定超过50ms(1/60s)。

简单描述一下这种做法:首先创建一个单例,单例中定义了几个数组,用来存要在runloop循环中执行的任务,然后为主线程的runloop添加一个CFRunLoopObserver,当主线程在 NSDefaultRunLoopMode 中执行完任务,即将睡眠前,执行一个单例中保存的一次图片渲染任务。关键代码看  DWURunLoopWorkDistribution 类即可。

总结:

主线程RunLoop切换到 UITrackingRunLoopMode 时,视图有过多的修改

这也就是上面介绍的RunLoop的使用,避免在主线程RunLoop切换到 UITrackingRunLoopMode 时,修改视图。

RunLoopWorkDistribution链接:

https://github.com/diwu/RunLoopWorkDistribution

如果感觉这篇文章不错可以点击在看:point_down:

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章