显示远超手机屏幕分辨率的大图时,传统加载方法会造成内存暴涨,主线程阻塞,甚至内存到峰值造成崩溃。可以使用CATiledLayer来显示大图,类似瓦片视图,将需要绘制的内容分割成许多小块,然后异步绘制相应的小块,这样就节约了处理时间和内存。
有以下优点
- 不需要计算分块显示的区域,直接提供,只需要根据这个区域计算图片相应区域,然后绘制即可。
- 在其他线程绘制,不会因为阻塞主线程而导致卡顿。
- 实现了只在屏幕区域内显示图片,屏幕外不会显示,而且移动时会自动绘制之前未绘制的区域,当你缩放时也会自动重绘。
CATiledLayer有三个重要属性:
levelsOfDetail:指该图层缓存的缩小LOD数目,默认值为1,每进一级会对前一级分辨率的一半进行缓存,图层的levelsOfDetail最大值,对应至少一个像素点。
levelsOfDetailBias:指该图层缓存的放大LODB数目,是layer的方法级别重绘设置,默认是0,不会额外缓存放大层次,每进一级会对前一级两倍分辨率进行缓存。
tileSize:layer划分视图区域最大尺寸,影响layer的切片数量。默认是256x256。
需要实现layerClass和drawRect方法,才能使用CATiledLayer设置参数,即可实现瓦片绘制效果。
代码如下:
- (void)setImageName:(NSString *)imageName {
[self lg_setImage:[UIImage imageNamed:imageName]];
}
- (void)lg_setImage:(UIImage *)image {
if (tiledCount == 0) tiledCount = 16;
originImage = image;
[self setBackgroundColor:[UIColor whiteColor]];
imageRect = CGRectMake(0.0f,
0.0f,
CGImageGetWidth(originImage.CGImage),
CGImageGetHeight(originImage.CGImage));
imageScale_w = self.frame.size.width/imageRect.size.width;
imageScale_h = self.frame.size.height/imageRect.size.height;
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
int scale = (int)MAX(1/imageScale_w, 1/imageScale_h);
int lev = ceil(scale);
tiledLayer.levelsOfDetail = 1;
tiledLayer.levelsOfDetailBias = lev;
if(tiledCount > 0){
NSInteger tileSizeScale = sqrt(tiledCount)/2;
CGSize tileSize = self.bounds.size;
tileSize.width /=tileSizeScale;
tileSize.height /=tileSizeScale;
tiledLayer.tileSize = tileSize;
}
}
+ (Class)layerClass {
return [CATiledLayer class];
}
- (void)drawRect:(CGRect)rect {
@autoreleasepool{
CGRect imageCutRect = CGRectMake(rect.origin.x / imageScale_w,
rect.origin.y / imageScale_h,
rect.size.width / imageScale_w,
rect.size.height / imageScale_h);
CGImageRef imageRef = CGImageCreateWithImageInRect(originImage.CGImage, imageCutRect);
UIImage *tileImage = [UIImage imageWithCGImage:imageRef];
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
[tileImage drawInRect:rect];
CGImageRelease(imageRef);
UIGraphicsPopContext();
}
}