美文网首页
Core Animation (一) contents

Core Animation (一) contents

作者: wufeifan890330 | 来源:发表于2016-07-06 16:35 被阅读110次

    一、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

    相关文章

      网友评论

          本文标题:Core Animation (一) contents

          本文链接:https://www.haomeiwen.com/subject/vtmkjttx.html