咱们来对颜色渐变的例子使用一个不一样的行为,经过给colorLayer设置一个自定义的actions字典。咱们也可使用委托来实现,可是actions字典能够写更少的代码。那么到底改如何建立一个合适的行为对象呢? git
行为一般是一个被Core Animation隐式调用的显式动画对象。这里咱们使用的是一个实现了CATransaction的实例,叫作推动过渡。 github
第八章中将会详细解释过渡,不过对于如今,知道CATransition响应CAAction协议,而且能够当作一个图层行为就足够了。结果很赞,不论在何时改变背景颜色,新的色块都是从左侧滑入,而不是默认的渐变效果。 app
清单7.6 实现自定义行为 dom
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *layerView; @property (nonatomic, weak) IBOutlet CALayer *colorLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //create sublayer self.colorLayer = [CALayer layer]; self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f); self.colorLayer.backgroundColor = [UIColor blueColor].CGColor; //add a custom action CATransition *transition = [CATransition animation]; transition.type = kCATransitionPush; transition.subtype = kCATransitionFromLeft; self.colorLayer.actions = @{@"backgroundColor": transition}; //add it to our view [self.layerView.layer addSublayer:self.colorLayer]; } - (IBAction)changeColor { //randomize the layer background color CGFloat red = arc4random() / (CGFloat)INT_MAX; CGFloat green = arc4random() / (CGFloat)INT_MAX; CGFloat blue = arc4random() / (CGFloat)INT_MAX; self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor; }
@end
图7.3 使用推动过渡的色值动画 动画
CALayer的属性行为其实很不正常,由于改变一个图层的属性并无马上生效,而是经过一段时间渐变动新。这是怎么作到的呢? atom
当你改变一个图层的属性,属性值的确是马上更新的(若是你读取它的数据,你会发现它的值在你设置它的那一刻就已经生效了),可是屏幕上并无立刻发生改变。这是由于你设置的属性并无直接调整图层的外观,相反,他只是定义了图层动画结束以后将要变化的外观。 spa
当设置CALayer的属性,其实是在定义当前事务结束以后图层如何显示的模型。Core Animation扮演了一个控制器的角色,而且负责根据图层行为和事务设置去不断更新视图的这些属性在屏幕上的状态。 code
咱们讨论的就是一个典型的微型MVC模式。CALayer是一个链接用户界面(就是MVC中的view)虚构的类,可是在界面自己这个场景下,CALayer的行为更像是存储了视图如何显示和动画的数据模型。实际上,在苹果本身的文档中,图层树一般都是值的图层树模型。 对象
在iOS中,屏幕每秒钟重绘60次。若是动画时长比60分之一秒要长,Core Animation就须要在设置一次新值和新值生效之间,对屏幕上的图层进行从新组织。这意味着CALayer除了“真实”值(就是你设置的值)以外,必需要知道当前显示在屏幕上的属性值的记录。 事务
每一个图层属性的显示值都被存储在一个叫作呈现图层的独立图层当中,他能够经过-presentationLayer方法来访问。这个呈现图层其实是模型图层的复制,可是它的属性值表明了在任何指定时刻当前外观效果。换句话说,你能够经过呈现图层的值来获取当前屏幕上真正显示出来的值(图7.4)。
咱们在第一章中提到除了图层树,另外还有呈现树。呈现树经过图层树中全部图层的呈现图层所造成。注意呈现图层仅仅当图层首次被提交(就是首次第一次在屏幕上显示)的时候建立,因此在那以前调用-presentationLayer将会返回nil。
你可能注意到有一个叫作–modelLayer的方法。在呈现图层上调用–modelLayer将会返回它正在呈现所依赖的CALayer。一般在一个图层上调用-modelLayer会返回–self(实际上咱们已经建立的原始图层就是一种数据模型)。
图7.4 一个移动的图层是如何经过数据模型呈现的
大多数状况下,你不须要直接访问呈现图层,你能够经过和模型图层的交互,来让Core Animation更新显示。两种状况下呈现图层会变得颇有用,一个是同步动画,一个是处理用户交互。
咱们能够用一个简单的案例来证实后者(见清单7.7)。在这个例子中,点击屏幕上的任意位置将会让图层平移到那里。点击图层自己能够随机改变它的颜色。咱们经过对呈现图层调用-hitTest:来判断是否被点击。
若是修改代码让 -hitTest: 直接做用于colorLayer而不是呈现图层,你会发现当图层移动的时候它并不能正确显示。这时候你就须要点击图层将要移动到的位置而不是图层自己来响应点击(这就是为何用呈现图层来响应交互的缘由)。
清单7.7 使用presentationLayer图层来判断当前图层位置
@interface ViewController () @property (nonatomic, strong) CALayer *colorLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //create a red layer self.colorLayer = [CALayer layer]; self.colorLayer.frame = CGRectMake(0, 0, 100, 100); self.colorLayer.position = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2); self.colorLayer.backgroundColor = [UIColor redColor].CGColor; [self.view.layer addSublayer:self.colorLayer]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //get the touch point CGPoint point = [[touches anyObject] locationInView:self.view]; //check if we've tapped the moving layer if ([self.colorLayer.presentationLayer hitTest:point]) { //randomize the layer background color CGFloat red = arc4random() / (CGFloat)INT_MAX; CGFloat green = arc4random() / (CGFloat)INT_MAX; CGFloat blue = arc4random() / (CGFloat)INT_MAX; self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor; } else { //otherwise (slowly) move the layer to new position [CATransaction begin]; [CATransaction setAnimationDuration:4.0]; self.colorLayer.position = point; [CATransaction commit]; } } @end
这一章讨论了隐式动画,还有Core Animation对指定属性选择合适的动画行为的机制。同时你知道了UIKit是如何充分利用Core Animation的隐式动画机制来强化它的显式系统,以及动画是如何被默认禁用而且当须要的时候启用的。最后,你了解了呈现和模型图层,以及Core Animation是如何经过它们来判断出图层当前位置以及将要到达的位置。
在下一章中,咱们将研究Core Animation提供的显式动画类型,既能够直接对图层属性作动画,也能够覆盖默认的图层行为。