一、contents
CALayer的contents属性,虽然被定义为id类型,貌似可以指向任何类型的对象,但是在实际中,如果给contents赋的不是CGImage,那么得到的layer显示的将是空白。
之所以定义为id,是为了兼容Mac OS,在Mac OS上,该属性对CGImage和NSImage都能生效。
二、contentsGravity
在使用UIImageView的时候,当我们加载的图片与imageView的大小比例不一致时,图片将会被拉伸。此时可以设置contentMode为UIViewContentModeScaleAspectFit。
同样的,在CALayer中,也有类似属性,即contentsGravity。该属性是个NSString,可选值为
kCAGravityCenter
kCAGravityTop
kCAGravityBottom
kCAGravityLeft
kCAGravityRight
kCAGravityTopLeft
kCAGravityTopRight
kCAGravityBottomLeft
kCAGravityBottomRight
kCAGravityResize
kCAGravityResizeAspect
kCAGravityResizeAspectFill
具体作用自测哈~~
三、contentsScale
contentsScale定义了content对应的图片的像素尺寸和视图大小的比例,默认为1。该属性属于支持高分辨率(Retina)屏幕机制的一部分。它用来判断在绘制图层的时候应该为寄宿图创建的空间大小,和需要显示
的图片的拉伸度(假设并没有设置 contentsGravity 属性)。UIView有一个类似功能但是非常少用到的 contentScaleFactor 属性。
如果contentsScale设置为1,将会以每个点1个像素绘制图片,如果设置为2,则会以每个点2个像素绘制图片,也就是我们常说的Retina屏幕。
此外,该属性与contentsGravity有时候会产生冲突,例如当我们设置contentsGravity为kCAGravityResizeAspect自动拉伸以适应图层后,设置contentsScale无效。
当我们用UIImage加载图片时,若图片为@2x图片,则UIImage的属性scale为2.因此一般设置contentsScale都是根据content(CGImage)对应的UIImage的scale来设置。
四、contentsRect
好啦,终点来了,其实这个才是真正想说的。
CALayer的contentsRect属性允许我们在contents中仅显示原图的一个子区域。
要注意,该属性虽然是一个CGRect类型,但其单位并非我们所熟知的点。它使用的是单位坐标,指定在0-1之间,是一个相对值(相对原图的宽高)。
附IOS用到的坐标系统简介:
点——在iOS和MacOS中最常见的坐标体系。点就像是虚拟的像素,也被称
作逻辑像素。在标准设备上,一个点就是一个像素,但是在Retina设备上,一
个点等于2*2个像素。iOS用点作为屏幕的坐标测算体系就是为了在Retina设备
和普通设备上能有一致的视觉效果。
像素——物理像素坐标并不会用来屏幕布局,但是仍然与图片有相对关系。
UIImage是一个屏幕分辨率解决方案,所以指定点来度量大小。但是一些底层
的图片表示如CGImage就会使用像素,所以你要清楚在Retina设备和普通设备
上,他们表现出来了不同的大小。
单位坐标——对于与图片大小或是图层边界相关的显示,单位坐标是一个方便的
度量方式,当大小改变的时候,也不需要再次调整。单位坐标在OpenGL这种
纹理坐标系统中用得很多,CoreAnimation中也用到了单位坐标。
默认的contentsRect是{0,0,1,1},也就是说整个原图默认可见,如果我们指定一个小一点的区域,那么layer上显示的将会是contents对应的原图中的一部分区域。比如设置为{0,0,0.5,0.5},如下图
![](https://img.haomeiwen.com/i2406313/efd1f671857425c8.png)
显示的是右侧的样纸~~~
contentsRect最有意义的地方在于一种称为image sprites(图片拼合)的技术。这在游戏开发中应用特别广泛。
该技术典型的用法是,将一系列图片整合到一张大图上一次性载入,相比多次载入多张小图,这样做极大的能减少载入时间以及少许的内存使用和渲染性能。
现在网上有许多图片整合工具,如TexturePacker等等。(友情提醒:该工具整合图片时,有个trim属性,设置是否将png图片透明区域去除,最好关掉哈~保持原来的切图。更具体的用法不清楚的可以留言哈~~)
假设我们通过工具已经将多张图片整合到一起了,那么相应的会生成一个数据文件,用来记录图片名、图片在当前大图中的位置等信息。如下图
![](https://img.haomeiwen.com/i2406313/ae5016510921a7c6.png)
我们就可以根据该数据文件,从大图中解析出我们要的每个图片了。
经过测试后,使用拼合图,加载速度基本可以提升几倍。
我自测时弄了1700多张小图,整合成大图后5M多。
4s读取5M的拼合图仅需0.002-0.006秒,而依次读取1700多张小图耗时4-5秒。读取并将图片渲染到界面上,拼合图需0.1-0.2秒,而依次读取小图4-5秒以上。【顺带也可以发现,渲染和加载图片的耗时占比,渲染貌似小得多呢~~】
当然,当图片量不多就没有必要啦~~~
所以,这技术也就在游戏开发中吃得开,毕竟游戏各种动效大部分都靠图片~需要大量的图片才玩得动~~
最后附上显示的核心代码。在这里我将加载大图到内存后,然后相应的把每个小图显示的scrollView上。
//加载拼合图
- (CGFloat)loadSubLayers
{
CGFloat totalHeight = 0;
CGFloat maxWidth = 0;
NSDate *begin = [NSDate date];
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:self.data[@"meta"][@"image"] ofType:nil]];
CGFloat imageWidth = CGImageGetWidth(image.CGImage);
CGFloat imageHeight = CGImageGetHeight(image.CGImage);
for (NSString *item in [self.data[@"frames"] allKeys]) {
@autoreleasepool {
#ifdef NEEDDRAW
NSDictionary *frameDict = self.data[@"frames"][item];
CGFloat width = [frameDict[@"w"] floatValue];
CGFloat height = [frameDict[@"h"] floatValue];
CGRect contentRect = CGRectMake([frameDict[@"x"] floatValue] / imageWidth, [frameDict[@"y"] floatValue] / imageHeight, width / imageWidth, height / imageHeight);
CALayer *layer = [CALayer layer];
layer.contents = (__bridge id _Nullable)(image.CGImage);
layer.contentsGravity = kCAGravityResizeAspect;
layer.contentsRect = contentRect;
layer.frame = CGRectMake(0, totalHeight, width, height);
layer.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1].CGColor;
totalHeight += layer.bounds.size.height;
maxWidth = MAX(maxWidth, layer.frame.size.width);
[self.scrollView.layer addSublayer:layer];
#endif
}
}
NSDate *end = [NSDate date];
CGFloat timeStamp = [end timeIntervalSinceDate:begin];
self.scrollView.contentSize = CGSizeMake(maxWidth, totalHeight);
return timeStamp;
}
//加载小图
- (CGFloat)loadSubLayers1
{
CGFloat height = 0;
CGFloat maxWidth = 0;
NSDate *begin = [NSDate date];
for (NSString *item in [self.data[@"frames"] allKeys]) {
@autoreleasepool {
NSString *imageName = item;
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:@"png"]];
#ifdef NEEDDRAW
CALayer *layer = [CALayer layer];
layer.contents = (__bridge id _Nullable)(image.CGImage);
layer.frame = CGRectMake(0, height, CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
layer.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1].CGColor;
height += layer.bounds.size.height;
maxWidth = MAX(maxWidth, layer.frame.size.width);
[self.scrollView.layer addSublayer:layer];
#endif
}
}
NSDate *end = [NSDate date];
CGFloat timeStamp = [end timeIntervalSinceDate:begin];
self.scrollView.contentSize = CGSizeMake(maxWidth, height);
return timeStamp;
}
网友评论