iOS核心动画高级技巧-1
1. 图层树
图层的树状结构
巨妖有图层,洋葱也有图层,你有吗?我们都有图层 -- 史莱克
Core Animation其实是一个令人误解的命名。你可能认为它只是用来做动画的,但实际上它是从一个叫做Layer Kit这么一个不怎么和动画有关的名字演变而来,所以做动画这只是Core Animation特性的冰山一角。
Core Animation是一个复合引擎,它的职责就是尽可能快地组合屏幕上不同的可视内容,这个内容是被分解成独立的图层,存储在一个叫做图层树的体系之中。于是这个树形成了UIKit以及在iOS应用程序当中你所能在屏幕上看见的一切的基础。
在我们讨论动画之前,我们将从图层树开始,涉及一下Core Animation的静态组合以及布局特性。
1.1 图层与视图
图层与视图
如果你曾经在iOS或者Mac OS平台上写过应用程序,你可能会对视图的概念比较熟悉。一个视图就是在屏幕上显示的一个矩形块(比如图片,文字或者视频),它能够拦截类似于鼠标点击或者触摸手势等用户输入。视图在层级关系中可以互相嵌套,一个视图可以管理它的所有子视图的位置。图1.1显示了一种典型的视图层级关系
1.2 图层的能力
图层的能力
如果说CALayer
是UIView内部实现细节,那我们为什么要全面地了解它呢?苹果当然为我们提供了优美简洁的UIView
接口,那么我们是否就没必要直接去处理Core Animation的细节了呢?
一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:1012951431, 分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!希望帮助开发者少走弯路。
某种意义上说的确是这样,对一些简单的需求来说,我们确实没必要处理CALayer
,因为苹果已经通过UIView
的高级API间接地使得动画变得很简单。
但是这种简单会不可避免地带来一些灵活上的缺陷。如果你略微想在底层做一些改变,或者使用一些苹果没有在UIView
上实现的接口功能,这时除了介入Core Animation底层之外别无选择。
我们已经证实了图层不能像视图那样处理触摸事件,那么他能做哪些视图不能做的呢?这里有一些UIView没有暴露出来的CALayer
的功能:
- 阴影,圆角,带颜色的边框
- 3D变换
- 非矩形范围
- 透明遮罩
- 多级非线性动画
我们将会在后续章节中探索这些功能,首先我们要关注一下在应用程序当中CALayer
是怎样被利用起来的。
1.3 使用图层
使用图层
首先我们来创建一个简单的项目,来操纵一些layer的属性。打开Xcode,使用Single View Application模板创建一个工程。
在屏幕中央创建一个小视图(大约200 X 200的尺寸),当然你可以手工编码,或者使用Interface Builder(随你方便)。确保你的视图控制器要添加一个视图的属性以便可以直接访问它。我们把它称作layerView。
运行项目,应该能在浅灰色屏幕背景中看见一个白色方块,如果没看见,可能需要调整一下背景window或者view的颜色
之后就可以在代码中直接引用CALayer的属性和方法。在清单1.1中,我们用创建了一个CALayer,设置了它的backgroundColor属性,然后添加到layerView背后相关图层的子图层(这段代码的前提是通过IB创建了layerView并做好了连接),图1.5显示了结果。
清单1.1 给视图添加一个蓝色子图层
#import "ViewController.h"
#import
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//create sublayer
CALayer *blueLayer = [CALayer layer];
blueLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
blueLayer.backgroundColor = [UIColor blueColor].CGColor;
//add it to our view
[self.layerView.layer addSublayer:blueLayer];
}
@end
1.4 总结
总结
这一章阐述了图层的树状结构,说明了如何在iOS中由UIView
的层级关系形成的一种平行的CALayer
层级关系,在后面的实验中,我们创建了自己的CALayer
,并把它添加到图层树中。
在第二章,“图层关联的图片”,我们将要研究一下CALayer
关联的图片,以及Core Animation提供的操作显示的一些特性。
2. 寄宿图
寄宿图
图片胜过千言万语,界面抵得上千图片 ——Ben Shneiderman
我们在第一章『图层树』中介绍了CALayer类并创建了一个简单的有蓝色背景的图层。背景颜色还好啦,但是如果它仅仅是展现了一个单调的颜色未免也太无聊了。事实上CALayer类能够包含一张你喜欢的图片,这一章节我们将来探索CALayer的寄宿图(即图层中包含的图)。
2.1 contents属性
contents属性
CALayer 有一个属性叫做contents
,这个属性的类型被定义为id,意味着它可以是任何类型的对象。在这种情况下,你可以给contents
属性赋任何值,你的app仍然能够编译通过。但是,在实践中,如果你给contents
赋的不是CGImage,那么你得到的图层将是空白的。
contents
这个奇怪的表现是由Mac OS的历史原因造成的。它之所以被定义为id类型,是因为在Mac OS系统上,这个属性对CGImage和NSImage类型的值都起作用。如果你试图在iOS平台上将UIImage的值赋给它,只能得到一个空白的图层。一些初识Core Animation的iOS开发者可能会对这个感到困惑。
头疼的不仅仅是我们刚才提到的这个问题。事实上,你真正要赋值的类型应该是CGImageRef,它是一个指向CGImage结构的指针。UIImage有一个CGImage属性,它返回一个"CGImageRef",如果你想把这个值直接赋值给CALayer的contents
,那你将会得到一个编译错误。因为CGImageRef并不是一个真正的Cocoa对象,而是一个Core Foundation类型。
尽管Core Foundation类型跟Cocoa对象在运行时貌似很像(被称作toll-free bridging),他们并不是类型兼容的,不过你可以通过bridged关键字转换。如果要给图层的寄宿图赋值,你可以按照以下这个方法:
layer.contents = (__bridge id)image.CGImage;
如果你没有使用ARC(自动引用计数),你就不需要 __bridge 这部分。但是,你干嘛不用ARC?!
让我们来继续修改我们在第一章新建的工程,以便能够展示一张图片而不仅仅是一个背景色。我们已经用代码的方式建立一个图层,那我们就不需要额外的图层了。那么我们就直接把layerView的宿主图层的contents
属性设置成图片。
清单2.1 更新后的代码。
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad]; //load an image
UIImage *image = [UIImage imageNamed:@"Snowman.png"];
//add it directly to our view's layer
self.layerView.layer.contents = (__bridge id)image.CGImage;
}
@end
图表2.1 在UIView的宿主图层中显示一张图片
image我们用这些简单的代码做了一件很有趣的事情:我们利用CALayer在一个普通的UIView中显示了一张图片。这不是一个UIImageView,它不是我们通常用来展示图片的方法。通过直接操作图层,我们使用了一些新的函数,使得UIView更加有趣了。
contentGravity
你可能已经注意到了我们的雪人看起来有点。。。胖 ==! 我们加载的图片并不刚好是一个方的,为了适应这个视图,它有一点点被拉伸了。在使用UIImageView的时候遇到过同样的问题,解决方法就是把contentMode
属性设置成更合适的值,像这样:
后续精彩内容请转到我的博客继续观看