Core Animation (一) contents

2016-07-06  本文已影响110人  wufeifan890330

一、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},如下图

显示的是右侧的样纸~~~

contentsRect最有意义的地方在于一种称为image sprites(图片拼合)的技术。这在游戏开发中应用特别广泛。

该技术典型的用法是,将一系列图片整合到一张大图上一次性载入,相比多次载入多张小图,这样做极大的能减少载入时间以及少许的内存使用和渲染性能。

现在网上有许多图片整合工具,如TexturePacker等等。(友情提醒:该工具整合图片时,有个trim属性,设置是否将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;

}

参考自:iOS-Core-Animation-Advanced-Techniques

上一篇下一篇

猜你喜欢

热点阅读