第二部分 核心动画基础---基础动画

免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!

著作权声明:本文由http://blog.csdn.net/mengtnt翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,

 

核心动画是一个强大成熟的技术,运用它可以使你达到事半功倍的效果。为了执行动画,苹果公司提供了动画的代理对象,当它被调用时同时那些可视的组件,譬如视图的外框、透明度、或者位置改变时,就会自动的触发隐式的动画。对于基础的层动画,CABasicAnimation类通过提供一个开始值和一个结束值,来执行一个动画。这章,通过你的应用程序,我们来看一些执行动画的基础方法。

最简单的动画

    由于核心动画集成到了cocoa中,因而你通过给窗口、视图、层简单的设定一些你感兴趣的属性值,从而使那些可视的组件做动画到新的值。当你使用CALayer的时候,你需要做的就是直接设置这些值。例如,如果你想改变一个层的边框大小,你简单的调用[layer setBounds:newFrame],这里要说的是,层是你已经创建的CALayer对象,并且要增加这个对象要层树中,而newFrame是一个CGRect包含了边框的尺寸和原点。当这些代码运行时,就会使用关键路径“bounds”的默认动画,使层具有边框大小改变时的动画。

简单的说,当你使用一个窗口(NSWindow)或者视图(NSView),你需要做的就是,使用了动画的代理对象,来设置窗口或者视图的属性值。这意味着,例如,你可以代替使用[view setFrame:newFrame]来设置窗口的框架,通过[[view animtor] setFrame:newFrame]这个函数调用,达到目的。不同之处就是,你用了一个动画的代理对象,设置了这些属性,而这些都会隐式的执行框架的初始值到结束值的动画。

动画的代理对象

因此什么是动画的代理对象哪?动画的代理在NSViewNSWindow上面都可用。它实现了NSAnimatablePropertyContainer的代理。这包含了使用键值编码对,当你需要做插值和在场景后面做动画时,来设定你指派的参数值。

顾名思义,动画代理就是一个媒介,来设定你给定的值,达到控制动画的属性,从开始或者目前的值,到结束的值。它设定了这些值,就像是你已经在这些属性上调用了一样。

在窗口、视图和层动画之间的不同

在窗口,视图和层之间的动画是一样的,然而实现却不同。这个段落中,我们将讨论你可能会想到的,最普通的动画的实现---框架的尺寸改变。

窗口尺寸改变

自从mac os x的第一个版本,动画一个窗口框架的能力已经可用,对于开发者来说,利用

-(void)setFrame:(NSRect)windowFrame

display:(BOOL)displayViews animate:(BOOL)performAnimation

来设定动画。第一个参数是你将要动画到的新框架。第二个参数告诉窗口在所有的子视图上面调用-displayIfNeeded,和第三个参数是告诉窗口以动画的方式,从目前的框架转换到新的框架。如果最后一个参数设定为NO,新的框架会立即改变,而不会有渐变的动画。

既然内嵌的窗口框架具有改变大小的能力,那么为什么你需要使用核心动画来改变窗口的框架那?答案是你不必。对于许多情况,当你改变尺寸的时候,你可能仅仅需要使用内嵌的功能就行了。然而,可能有这些情况,你想要在窗口动画上有更多的控制。当你要做这些的时候,记住一些事情。NSWindow就像NSView一样,有一个动画代理。当你调用动画者时,它会按照你指定的参数做动画,但是那些参数都是附带物。如果你想把一个窗口,在屏幕上移动到一个不同的位置,事实上,要么你用 

- (void)setFrame:(NSRect)windowFrame

display:(BOOL)displayViews

这个方法(这个方法不要第三个参数),或者你可以增加动画到窗口本身的动画字典中。首先让我们看怎么来使用动画代理。如下:

[[window animator] setFrame:newFrame display:YES];

这个可以使你简单的动画窗口的框架。

默认的动画播放是0.25秒。如果你想改变这个时间,需要使用NSAnimationContext这个对象,对于CATransaction它是NSView/NSWindow的副本。如果我们利用NSAnimationContext封装调用-setFrame,我们可以指定动画的时间。表3-1演示了如何做:

[plain]  view plain copy
  1. [NSAnimationContext beginGrouping];  
  2.   
  3. [[NSAnimationContext currentContext] setDuration:5.0f];  
  4.   
  5. [[window animator] setFrame:newFrame display:YES];  
  6.   
  7. [NSAnimationContext endGrouping];  

List 3-1

这引起了框架的改变的时间为5秒,而非默认的0.25秒。你下一段即将看到,这种方法也是你能使用的,可以改变NSView的动画时间的。

使用核心动画的CABasicAnimation 也能够在窗口和视图上做动画,但是动画如何安装有点不同。作为通过在窗口动画代理调用-setFrame的替换者,我可以创建一个CABasicAnimation和动画框架的属性。下面在表3-2中可以看到如何在窗口创建、增加和运行基础动画。

[plain]  view plain copy
  1. CABasicAnimation *animation =  
  2.   
  3. [CABasicAnimation animationWithKeyPath:@”frame”];  
  4.   
  5. [animation setFromValue:[NSValue valueWithRect:oldFrame]];  
  6.   
  7. [animation setToValue:[NSValue valueWithRect:newFrame]];  
  8.   
  9. [animation setDuration:5.0f];  
  10.   
  11. [window setAnimations:[NSDictionary animation forKey:@”frame”]];  
  12.   
  13. [[window animator] setFrame:newFrame display:YES];  

 

             表3-2 增加一个动画到窗口动画字典

这个动画的效果和表3-1是相同的。

视图的改变

视图和窗口同样能被改变,但是你要使用不同的关键路径。你可以在视图上使用-setFrame这个在窗口上使用的同样的代码。如图3-3

[plain]  view plain copy
  1. [NSAnimationContext beginGrouping];  
  2.   
  3. [[NSAnimationContext currentContext] setDuration:5.0f];  
  4.   
  5. [[view animator] setFrame:newFrame display:YES];  
  6.   
  7. [NSAnimationContext endGrouping];  

 

                        表3-3

仅仅的不同之处是,表3-1中调用的对象不同,这里是用的view

如果你想使用显示的动画,代替使框架做动画,你可以使框架原点和框架尺寸做动画。表3-4展示了如何使用这些属性做动画。

[plain]  view plain copy
  1. CABasicAnimation *originAnimation = [CABasicAnimation  
  2.   
  3. animationWithKeyPath:@”frameOrigin”];  
  4.   
  5. [originAnimation setFromValue:[NSValue  
  6.   
  7. valueWithPoint:oldImageFrame.origin]];  
  8.   
  9. [originAnimation setToValue:[NSValue valueWithPoint:newFrame.origin]];  
  10.   
  11. [originAnimation setDuration:5.0];  
  12.   
  13. CABasicAnimation *sizeAnimation = [CABasicAnimation  
  14.   
  15. animationWithKeyPath:@”frameSize”];  
  16.   
  17. [sizeAnimation setFromValue:  
  18.   
  19. [NSValue valueWithSize:oldImageFrame.size]];  
  20.   
  21. [sizeAnimation setToValue:[NSValue valueWithSize:newFrame.size]];  
  22.   
  23. [sizeAnimation setDuration:5.0];  
  24.   
  25. [[view animator] setAnimations:[NSDictionary  
  26.   
  27. dictionaryWithObjectsAndKeys:originAnimation,  
  28.   
  29. @”frameOrigin”,  
  30.   
  31. sizeAnimation,  
  32.   
  33. @”frameSize”,  
  34.   
  35. nil]];  
  36.   
  37. [[view animator] setFrame:newFrame];  

 

                 表3-4显示的使原点和尺寸做动画

层的尺寸改变

一个层的框架的动画是有点不同于窗口和视图的动画。在CALayer对象中没有动画代理可用,但是也并非当你想有一个显示的改变动画的属性时,动画总是被调用。事实上,如果你想禁用动画,你就得显示的关闭动画。表3-5演示了如何做。

[plain]  view plain copy
  1. [CATransaction begin]  
  2.   
  3. [CATransaction setValue:[NSNumber numberWithBool:YES]  
  4.   
  5. forKey: kCATransactionDisableActions]  
  6.   
  7. [layer setBounds:bounds];  
  8.   
  9. [CATransaction commit];  

 

            表 3-5 显示的关闭动画

CATransaction类和我们在表3-23-4对于窗口和视图中的APPKit中使用的NSAnimationContext非常的相似。就像NSAnimationContext一样,CATransaction也可以给动画设置时间。表3-6演示了如何做:

[plain]  view plain copy
  1. [CATransaction begin]  
  2.   
  3. [CATransaction setValue:[NSNumber numberWithFloat:5.0f]  
  4.   
  5. forKey: kCATransactionAnimationDuration]  
  6.   
  7. [layer setBounds:bounds];  
  8.   
  9. [CATransaction commit];  

 

                     表 3-6

就像你猜想的,我们也能显示的使用层的属性做动画。表3-7是来完成表3-6同样的效果的代码。

[plain]  view plain copy
  1. CABasicAnimation *boundsAnimation = [CABasicAnimation  
  2.   
  3. animationWithKeyPath:@”bounds”];  
  4.   
  5. [boundsAnimation setFromValue:[NSValue valueWithRect:oldRect]];  
  6.   
  7. [boundsAnimation setToValue:[NSValue valueWithRect:newRect]];  
  8.   
  9. [boundsAnimation setDuration:5.0f];  
  10.   
  11. [layer setBounds:NSRectToCGRect(newRect)];  
  12.   
  13. [layer addAnimation:boundsAnimation forKey:@”bounds”];  

 

                       表3-7

在表3-6中,我们也使用了CABasicAnimation这个类,它是基础动画的主要类。马上我们会深入的理解这个类,但是首先我们要安装一个简单的Xcode的工程来演示基础的层动画。

准备一个视图来做层动画

当你创建一个基于核心动画的工程时,你要做的第一件事是确保视图的根层有一个后背的层。下面让我们一步一步创建基于核心动画的工程,并且在OS X上安装一个根层。

创建XCode工程

去创建应用程序,按照下面的步骤:

1.在Xcode中,按shift - command - N和在工程模板中,选择cocoa应用程序。

2.命名工程叫CA Basics,点击保存。

3.扩展frameworks组,control - click链接framework子组,并且选择add>existing frameworks

4.在结果对话框中,导航到/system/library/frameworks,然后选择QuartzCore.framework。按照提示,点击增加2次。

5.Control - click class 组,选择add>New File

6.在新的模板对话框中,下面的cocoa组中选择Objective-c 类,然后点击next

7.命名文件为appDelegate.m,并且核对确保创建了appDelegate.h。然后点击完成

8.选择appDelegate.h在编辑器中打开文件,并且增加下面的代码

[plain]  view plain copy
  1. @interface AppDelegate : NSObject {  
  2.   
  3. IBOutlet NSWindow *window;  
  4.   
  5. }  

 

9.选择appDelegate.m在编辑器中打开文件,增加下面的代码

[plain]  view plain copy
  1. @implementation AppDelegate  
  2.   
  3. - (void)awakeFromNib;  
  4.   
  5. {  
  6.   
  7. [[window contentView] setWantsLayer:YES];  
  8.   
  9. }  
  10.   
  11. @end  

 

10.在工程的Resources组下面,双击MainMenu.xib,在接口编辑器中打开XIB文件。

11.从Library画板中,拖出NSObject对象到MainMenu.xib中,重命名它为appDelegate

12.确保AppDelegate对象被选择,在对象编辑器中,点击Identity 标签并且改变类的域为AppDelegate

13.在MainMenu.xib中,control - click File's Owner 并且拖拽链接到Appdelegate对象中。在下拉的按钮中选择delegate

14.在MainMenu.xib中,control - click Appdelegate和拖拽链接到Window object。在下拉菜单中选择window

15.保存xib文件和返回XCode

工程时安装完毕。在先前的步骤中,我们创建了一个应用程序的代理,这个代理是用来控制我们的层、窗口、和视图的。

给根层增加动画层

去增加我们将要动画的层,做法如下:

1.打开AppDelegate.h和增加一个CALayer实例变量:

[plain]  view plain copy
  1. @interface AppDelegate : NSObject  
  2.   
  3. {  
  4.   
  5. IBOutlet NSWindow *window;  
  6.   
  7. CALayer *layer;  
  8.   
  9. }  

 

2.打开AppDelegate.m,并且增加层的初始化代码在-awakeFromNib:

[plain]  view plain copy
  1. @implementation AppDelegate  
  2.   
  3. - (void)awakeFromNib;  
  4.   
  5. {  
  6.   
  7. [[window contentView] setWantsLayer:YES];  
  8.   
  9. layer = [CALayer layer];  
  10.   
  11. [layer setBounds:CGRectMake(0.0, 0.0, 100.0, 100.0)];  
  12.   
  13. // Center the animation layer  
  14.   
  15. [layer setPosition:CGPointMake([[window contentView]  
  16.   
  17. frame].size.width/2,  
  18.   
  19. [[window contentView]  
  20.   
  21. frame].size.height/2)];  
  22.   
  23. CGColorRef color = CGColorCreateGenericRGB(0.4, 0.3, 0.2, 1);  
  24.   
  25. [layer setBackgroundColor:color];  
  26.   
  27. CFRelease(color);  
  28.   
  29. [layer setOpacity:0.75];  
  30.   
  31. [layer setBorderWidth:5.0f];  
  32.   
  33. [[[window contentView] layer] addSublayer:layer];  
  34.   
  35. }  
  36.   
  37. @end  

 

层内存分配的注意事项

尽管你有了一个实例变量,但是当你安装完层后,另一个你应该意识到的是,这个层它没有retained,除非你显示的retain了它。在objective - c的内存管理中的规则,是在你仅仅需要的地方retain。如果你不想长久的持有他,你不应该retain一个对象,而你需要持有时,你就应该retain这个对象。

这听起来简单,但是实际用起来比较复杂。在先前的代码中,你看到了我为层开辟空间,用了一个便捷的方法layer = [CALayer layer];这会给CAlayer对象分配一个auto-released的对象。当这个层对象离开了-awakeFromNid这个代码段空间时,它将自动释放,除非你retain了它。在我的例子中,我们增加了层到contentView的子层数组中,这样就给我们retain了层。然而,假如我们要等到真在-awakeFromNib中初始化时,增加的层到子层的数组中,我们需要用[[CALayer alloc]init]这个方法来初始化。然而我们需要释放这个层,在dealloc方法中调用[layer release]

你只要一次使用了-removeFromSuperlayer这个方法,你会发现,如果你再试图增加层到子层中时,它将会使你的应用程序crash。这是因为当调用-removeFromSuperLayer时,层将会被释放。假如你想从它的父层中,移除它的子层,但是还想保留它在内存中,那么你就必须retain这个层。

使用CABasicAnimation

这里你已经看到了CABasicAnimation对象的行动。然而,在这个段落里,我们会考虑一些细节,如何来使用这些类和基础动画。

使用CABasicAnimation类实现的基础动画,是通过2个值,一个开始的,一个结束的来做动画的。例如,为了在窗口中,移动从一个点到另一个点,我们能够使用关键路径position创建一个基础动画。我们可以给动画一个开始的值和一个结束的值,然后增加动画到层中。在下一个运行循环中,那个动画就可以立即执行。表3-8演示了如何来对一个层的位置做动画。

[plain]  view plain copy
  1. - (IBAction)animate:(id)sender;  
  2.   
  3. {  
  4.   
  5. CABasicAnimation *animation =  
  6.   
  7. [CABasicAnimation animationWithKeyPath:@”position”];  
  8.   
  9. [animation setFromValue:[NSValue valueWithPoint:startPoint]];  
  10.   
  11. [animation setToValue:[NSValue valueWithPoint:endPoint]];  
  12.   
  13. [animation setDuration:5.0];  
  14.   
  15. [layer addAnimation:animation forKey:@”position”];  
  16.   
  17. }  

 

这个代码移动一个层的位置,从startPointendPoint。这两个值都是NSPoint对象。Position属性是一个层的中心点。它和它包含的层有关系。

如果你增加了下表中的代码到我们先前创建的工程中,你就可以在接口编辑器中,简单的链接一个按钮的行动。跟着下面的步骤做:

1.打开AppDelegate.h,然后增加响应声明如下:

[plain]  view plain copy
  1. @interface AppDelegate : NSObject  
  2.   
  3. {  
  4.   
  5. IBOutlet NSWindow *window;  
  6.   
  7. CALayer *layer;  
  8.   
  9. }  
  10.   
  11. - (IBAction)animate:(id)sender;  

 

2.打开AppDelegate.m和增加animate的实现代码如表3-8

3.打开接口编辑器。从对象库中,拖出按钮到main window上。

4.Control - click拖拽在main window上的按钮,然后链接到appDelegate对象上。选择animate这个行动

5.返回Xcode,编译运行,看效果。

就这些了。这真的就是给层创建动画的全部了。你创建一个动画,设置开始值和结束值,设置动画时间(如果不设置的话,默认是0.25秒)。然后就增加这个动画到你想要做动画的层上。

   还要继续说,因为实现的细节增加了一个微小的不同和复杂性,这里你最好不要立刻停下来。例如,当你用表3-8的代码,第一次运行动画时,你会注意到随着你指定的动画时间,层移动到了正确的位置,这时动画完成,它就会跳回到原来的位置。这是个bug么?我们怎么解决那?下面进一步分析。

动画vs层的属性

当你创建一个CABasicAnimation时,你需要通过-setFromValue-setToValue来指定一个开始和结束的值。当你增加基础动画到层中的时候,它开始运行。当用属性做动画完成时,例如用位置属性做动画,,层就会立刻返回到它的初始位置。

记住当你做动画时,你至少使用了2个对象。这些对象都是层本身,一个层或者层继承的对象,和在先前的例子中你分配给层的CABasicAnimation对象。因为你给动画对象设定了最后的值(目的地),但是并不意味着当动画完成的时候,层的属性就改变成了最后的值。当你动画完成时,你必须显示的设定层的属性,这样动画结束后,你的层才能真正的到你设定的属性值上。

你可以简单的停止动画到你结束的点上,但是这仅仅是一个视觉效果。层实际的值仍然是一样的。要真的改变内部的值,就像刚才所说的你必须显示的设定那个属性。例如,显示的设定位置的属性,你需要在层中调用-setPosition方法。但是,这会造成一点问题。

如果你通过-set这个方法显示的设定了层属性的值,那么默认的动画将被执行,而非之前你设定的动画。在表3-9中演示了你设置位置的方法。注意到了,我们使用position已经创建了基础动画,但是我们在层上显示的调用了-setPosition方法,就覆盖了我们设定的动画,使我们设定的基础动画完全没用了。如果你使用了这个代码,你会看到虽然我们的层结束的时候放到了正确的位置,但是它使用的是默认的0.25秒,而非我们在动画里显示设定的5秒钟。

[plain]  view plain copy
  1. CABasicAnimation *animation =  
  2.   
  3. [CABasicAnimation animationWithKeyPath:@”position”];  
  4.   
  5. [animation setFromValue:[NSValue valueWithPoint:startPoint]];  
  6.   
  7. [animation setToValue:[NSValue valueWithPoint:endPoint]];  
  8.   
  9. [animation setDuration:5.0];  
  10.   
  11. [layer setPosition:endpoint];  
  12.   
  13. [layer addAnimation:animation forKey:nil];  

 

              表3-9 动画和升级位置属性

因此现在问题出来了,你怎么能使用我们设定的动画那?看表3-9的最后一行,注意到forKey:这个参数是被设定为nil。这就是为什么动画不能覆盖默认动画的原因。如果你改变最后一行为[layer addAnimation:animation forKey:@"position"],动画将会按照我们设定的时间工作。这告诉了层当需要做动画时,使用我们给关键路径指定的新动画。

隐式的层动画和默认的时间步调

像我们先前章节做的,我们可以使用CATransaction类来重载默认的时间,并且它可以方便的使用我们指定的时间做动画。如果你使用表3-10的代码,position属性是被设置在层理,那个属性的动画就按照你期望的方式运行。

[plain]  view plain copy
  1. [CATransaction begin];  
  2.   
  3. [CATransaction setValue:[NSNumber numberWithFloat:5.0]  
  4.   
  5. forKey:kCATransactionAnimationDuration];  
  6.   
  7. [layer setPosition:endPoint];  
  8.   
  9. [CATransaction commit];  

 

            表3-10 隐式的重载了动画的时间

然而,当你运行这个代码时,你会看到动画的时间确实是5秒,但是它应用了默认的时间步调,就是KCAMediaTimingFunctionEaseInEaseOut。这个会引起开始的时候变慢,然后加速,之后再慢下了从而到达目的地。如果这是你想要的时间步调再好不过了,但是如果你想要一个线性的步调(KCAMEdiaTimingFunctionLinear),例如,你需要考虑其他的方法。没有直接的方法给隐式的动画设定时间步调。

这意味着,如果你想用其他的时间步调,你不得不用显示的动画,就像表3-9演示的那样。

视觉粘性

我们可以使用另一个方法,通过设定我们动画对象的属性,来达到动画完成时,固定在完成位置的效果。换句话说,层会展现为目的值。这个方案仅仅是视觉效果,也就是说那个层根本的位置还是动画开始时的位置。当你不需要真正的改变层的值时,这是一个很好的方法。表3-11展示了如何去实现这个方法,使层固定在结束的位置。

[plain]  view plain copy
  1. CABasicAnimation *animation = [CABasicAnimation  
  2.   
  3. animationWithKeyPath:@”position”];  
  4.   
  5. [animation setToValue:[NSValue valueWithPoint:endPoint]];  
  6.   
  7. [animation setDuration:5.0];  
  8.   
  9. [animation setFillMode:kCAFillModeForwards];  
  10.   
  11. [animation setRemovedOnCompletion:NO];  
  12.   
  13. [layer addAnimation:animation forKey:@”position”];  

 

            表3-11 使层的位置固定

我们需要设定2个动画属性。首先是填充模式。我们告诉动画来固定属性值为最后的值,通过调用-setFillMode方法,给它传递一个kCAFillModeForwards属性来实现。然后我们必须告诉动画,当动画结束的时候,不要动画从层的动画数组中移除。用-setRemoveOnCompletion方法,传递NO来实现。

有用的动画属性

   你已经发现了,所有可以在层上做动画的属性。然而,在动画对象(CABasicAnimation)中还有许多有用的属性,这些属性可以让你便于控制动画,提升动画的性能。

Autoreverses

当你设定这个属性为YES时,在它到达目的地之后,动画的属性会返回到开始的值,代替了直接跳转到开始的值。

Duration

Duration这个参数你已经相当熟悉来了。它设定开始值到结束值花费的时间。期间会被速度的属性所影响。

RemovedOnCompletion

这个属性默认为YES,那以为着,在指定的时间段完成后,动画就自动的从层上移除了。这个一般不用。假如你想要再次用这个动画时,你需要设定这个属性为NO。这样的话,下次你在通过-set方法设定动画的属性时,它将再次使用你的动画,而非默认的动画。

Speed

默认的值为1.0.这意味着动画播放按照默认的速度。如果你改变这个值为2.0,动画会用2倍的速度播放。这样的影响就是使持续时间减半。如果你指定的持续时间为6秒,速度为2.0,动画就会播放3秒钟---一半的持续时间。

BeginTime

这个属性在组动画中很有用。它根据父动画组的持续时间,指定了开始播放动画的时间。默认的是0.0.组动画在下个段落中讨论“Animation Grouping”。

TimeOffset

如果一个时间偏移量是被设定,动画不会真正的可见,直到根据父动画组中的执行时间得到的时间都流逝了。

RepeatCount

默认的是0,意味着动画只会播放一次。如果指定一个无限大的重复次数,使用1e100f。这个不应该和repeatDration属性一块使用。

RepeatDuration

这个属性指定了动画应该被重复多久。动画会一直重复,直到设定的时间流逝完。它不应该和repeatCount一起使用。

动画组

在先前的段落中,“有用的动画属性”,我们定义了2个特殊的属性,是关系到动画组的:beginTimetimeOffset。在我们讨论这个之前,然而,让我们考虑为什么你想要使用动画组,而非给层增加一列动画。

在表3-12中,你可以看到我们建立了一列基础动画,和简单的增加他们到层上面。如果你想要所有的动画开始在同样的时间,并且他们中每个动画都有同样的执行时间,这个方法是足够了。

[plain]  view plain copy
  1. - (IBAction)animate:(id)sender;  
  2.   
  3. {  
  4.   
  5. NSRect oldRect = NSMakeRect(0.0, 0.0, 100.0, 100.0);  
  6.   
  7. NSRect newRect = NSMakeRect(0.0, 0.0, 300.0, 300.0);  
  8.   
  9. CABasicAnimation *boundsAnimation =  
  10.   
  11. [CABasicAnimation animationWithKeyPath:@”bounds”];  
  12.   
  13. [boundsAnimation setFromValue:[NSValue valueWithRect:oldRect]];  
  14.   
  15. [boundsAnimation setToValue:[NSValue valueWithRect:newRect]];  
  16.   
  17. [boundsAnimation setDuration:5.0f];  
  18.   
  19. CABasicAnimation *positionAnimation =  
  20.   
  21. [CABasicAnimation animationWithKeyPath:@”position”];  
  22.   
  23. [positionAnimation setFromValue:  
  24.   
  25. [NSValue valueWithPoint:  
  26.   
  27. NSPointFromCGPoint([layer position])]];  
  28.   
  29. [positionAnimation setToValue:  
  30.   
  31. [NSValue valueWithPoint:NSMakePoint(0.0, 0.0)]];  
  32.   
  33. [positionAnimation setDuration:5.0f];  
  34.   
  35. CABasicAnimation *borderWidthAnimation =  
  36.   
  37. [CABasicAnimation animationWithKeyPath:@”borderWidth”];  
  38.   
  39. [borderWidthAnimation setFromValue:[NSNumber numberWithFloat:5.0f]];  
  40.   
  41. [borderWidthAnimation setToValue:[NSNumber numberWithFloat:30.0f]];  
  42.   
  43. [borderWidthAnimation setDuration:5.0f];  
  44.   
  45. [layer addAnimation:boundsAnimation forKey:@”bounds”];  
  46.   
  47. [layer addAnimation:positionAnimation forKey:@”position”];  
  48.   
  49. [layer addAnimation:borderWidthAnimation forKey:@”borderWidth”];  
  50.   
  51. }  


                                                            表3-12

每个动画都有5秒的执行时间,并且它们在下个循环里一起播放,最后同时结束。层的位置到左下角,层的边框宽度增加30个像素,和层的尺寸增加从100x100像素到了300x300像素。

让我们说下我们要做的情况,并不是使所有的动画同时播放,我们想要它们按顺序播放--之前定义好的顺序。我们可以完成这些,通过使用动画组合设定beginTime这个属性的区域。这里我提下,这种情况下如果我们使用关键帧可能更有意义,但是你需要读到第四章“Keyframe Animation”了解他怎么工作。

我们必须显示的指定我们组动画的执行时间,以便于能为每个动画分离一部分时间。例如,我们设定我们的动画时间为15秒钟,然后给每个动画5秒钟的播放时间。列表3-13展示了先前的例子,这里用动画组替代,来更好的控制每个动画的播放。

[plain]  view plain copy
  1. - (IBAction)animate:(id)sender;  
  2.   
  3. {  
  4.   
  5. NSRect oldRect = NSMakeRect(0.0, 0.0, 100.0, 100.0);  
  6.   
  7. NSRect newRect = NSMakeRect(0.0, 0.0, 300.0, 300.0);  
  8.   
  9. CABasicAnimation *boundsAnimation =  
  10.   
  11. [CABasicAnimation animationWithKeyPath:@”bounds”];  
  12.   
  13. [boundsAnimation setFromValue:[NSValue valueWithRect:oldRect]];  
  14.   
  15. [boundsAnimation setToValue:[NSValue valueWithRect:newRect]];  
  16.   
  17. [boundsAnimation setDuration:15.0f];  
  18.   
  19. [boundsAnimation setBeginTime:0.0f];  
  20.   
  21. CABasicAnimation *positionAnimation =  
  22.   
  23. [CABasicAnimation animationWithKeyPath:@”position”];  
  24.   
  25. [positionAnimation setFromValue:  
  26.   
  27. [NSValue valueWithPoint:  
  28.   
  29. NSPointFromCGPoint([layer position])]];  
  30.   
  31. [positionAnimation setToValue:  
  32.   
  33. [NSValue valueWithPoint:NSMakePoint(0.0, 0.0)]];  
  34.   
  35. [positionAnimation setDuration:15.0f];  
  36.   
  37. [positionAnimation setBeginTime:5.0f];  
  38.   
  39. CABasicAnimation *borderWidthAnimation =  
  40.   
  41. [CABasicAnimation animationWithKeyPath:@”borderWidth”];  
  42.   
  43. [borderWidthAnimation setFromValue:[NSNumber numberWithFloat:5.0f]];  
  44.   
  45. [borderWidthAnimation setToValue:[NSNumber numberWithFloat:30.0f]];  
  46.   
  47. [borderWidthAnimation setDuration:15.0f];  
  48.   
  49. [borderWidthAnimation setBeginTime:10.0f];  
  50.   
  51. CAAnimationGroup *group = [CAAnimationGroup animation];  
  52.   
  53. [group setDuration:15];  
  54.   
  55. [group setAnimations:  
  56.   
  57. [NSArray arrayWithObjects:boundsAnimation,  
  58.   
  59. positionAnimation,  
  60.   
  61. borderWidthAnimation, nil]];  
  62.   
  63. [layer addAnimation:group forKey:nil];  
  64.   
  65. }  

     表3-13 使用动画组

注意到我们为每个分割的动画都设定了15秒的执行时间,但是每个动画的开始时间分别为 0.0,5.0,和10.0.

你也注意到了我们仅仅增加组动画给层。组动画对象通过调用-setAnimations这个方法增加。

你可以看到使用组给予了你多么灵活的方法。你仅仅需要按照你的需求关注于你的执行时间和开始时间。如果你想要动画同时发生,你仅仅需要改变开始时间,就是你想要播放的开始时间。你要保持同样的执行时间,否则在层中每个关键路径(keyPaht)的值(就是,boundsposition,和borderWidth),在预期结束时,都会突兀的返回到开始的值,看起来很古怪。保持所有的执行时间都一样,可以使他们等待直到动画结束时,才返回到原来的值。如果你不想他们返回,在动画结束时,你需要显示的设定上一段落中提到的属性值“使用CABasicAnimation

总结

基础动画是非常强大的。为了完成动画的目标,你有很多便捷的方法。通常你都不需要用基础动画额外的方法。如果你需要的全部就是动画代理,使用它,保持简单!如果你需要的就是设置一个层的属性,就调用层属性的set方法让核心动画控制其余的工作。如果你需要更灵活的动画参数,使用CABasicAnimation对象,设定所有的动画属性。仅仅记得通常情况下,你仅仅需要基础动画就可以了。

你可能感兴趣的:(第二部分 核心动画基础---基础动画)