iOS 加载大图优化思路
2019-10-26 本文已影响0人
某非著名程序员
场景:假如有一张非常大的图片,可能有500MB,也可能有1个G甚至更大。需要显示在我们的iOS设备,该怎样加载呢?
直接读取图片,加载到UIImageView会直接闪退。
苹果的开发者早就考虑到这个,给开发者提供了CATiledLayer了。CATiledLayer 为载入大图造成的性能问题提供了一个解决方案:将大图分解成小片然后将他们单独按需载入。在多个线程中为每个小块同时调用 -drawLayer:inContext: 方法。这就避免了阻塞用户交互而且能够利用多核心新片来更快地绘制。只有一个小块的 CATiledLayer 是实现异步更新图片视图的简单方法。

案例中是一张2048*2048的图片,被分割成了64个256*256的小图,以Snowman_00_00-Snowman_07_07的规则命名。下面看CATiledLayer具体实现代码:
@interface CATiledLayerViewController ()<CALayerDelegate>
{
CATiledLayer * _tileLayer;
}
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@end
@implementation CATiledLayerViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
CATiledLayer *tileLayer = [CATiledLayer layer];
_tileLayer = tileLayer;
tileLayer.frame = CGRectMake(0, 0, 2048, 2048);
tileLayer.delegate = self;
tileLayer.drawsAsynchronously = YES;
tileLayer.contentsScale = [UIScreen mainScreen].scale;
[self.scrollView.layer addSublayer:tileLayer];
//configure the scroll view
self.scrollView.contentSize = tileLayer.frame.size;
//draw layer
[tileLayer setNeedsDisplay];
}
- (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)ctx
{
//determine tile coordinate
CGRect bounds = CGContextGetClipBoundingBox(ctx);
CGFloat scale = [UIScreen mainScreen].scale;
NSInteger x = floor(bounds.origin.x / layer.tileSize.width * scale);
NSInteger y = floor(bounds.origin.y / layer.tileSize.height * scale);
//load tile image
NSString *imageName = [NSString stringWithFormat: @"Snowman_%02li_%02li", (long)x, (long)y];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"];
UIImage *tileImage = [UIImage imageWithContentsOfFile:imagePath];
//draw tile
UIGraphicsPushContext(ctx);
[tileImage drawInRect:bounds];
UIGraphicsPopContext();
}
- (void)dealloc{
if (_tileLayer) {
[_tileLayer removeFromSuperlayer];
_tileLayer = nil;
}
}
@end
使用CATiledLayer需要注意:
1.退出页面时需要移除_tileLayer对象
2.计算x,y时需要考虑屏幕分辩率
总结:
1.在真正的业务中,需要的肯定是网络图片。CATiledLayer可与SDWebImage结合使用,并发下载和图片缓存的问题不需要再考虑。同时结合RunLoop的优化特性,只在空闲时加载图片。以达到性能最优。
2.而图片的切割及命名,应该在上传的时候就应该考虑的,即使是1个G的图片,我们也能先显示部分再根据滑动加载。
3.像分辨率,图片大小及分割完的url数组可以在接口统一返回。
本文参考:《iOS CoreAnimation》