欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

iOS: 提升加载大图效率的策略与方法

最编程 2024-07-27 18:56:50
...

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


加载大图优化.gif

  案例中是一张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》

推荐阅读