Core Animation Programming Guide - Advanced Animation Tricks

Advanced Animation Tricks

有许多方式可以配置你的 property-based 或 keyframe 动画,这些方式可以为你做很多。若想让 App 同时或者按顺序表现多个动画,你可以使用更多高级的行为来同步这些动画的时间或者动画的链条。你也可以使用其他动画类型对象创建视觉过度和其他有意思的动画效果。


Transition Animations Support Changes to Layer Visibility

就像名字一样,一个过度动画对象为一个 layer 创建一个动画的视觉过度。最平常的过度对象使用是在一个坐标行为中一个 layer 的显现和另一个 layer 的消失。不像 property-based 动画,在动画改变 layer 的属性的地方,一个过度动画操纵一个 layer 的缓存图像以创建视觉效果,单靠改变属性是很困难或者不可能的。标准的过度类型使你表现重现、推、移动或者淡入淡出动画。在 OS X 中,你也可以使用 Core Image 滤镜创建过度,使用其他影响类型,比如扫、翻页、波纹、或者你设计的自定义效果。

为了创建一个过度动画,你要创建一个 CATransition 对象,而且要把它添加到过度中包括的 layer。你使用过度对象明确过度类型,表现、开始和结束过度动画的点。你不需要使用整个的过度动画。当动画时,过度对象使你明确起始进度值的使用。这些值使你在动画的中点做一些事情,比如开始或结束这个动画。

代码 5-1 展示了在两个视图之间创建一个 push 过渡动画的代码。在这个例子中,myView1 和 myView2 位于同一个父层视图中,位置都是相同的,只是 myView1 是当前可见的视图。push 过渡动画导致 myView1 向左淡出直至消失,同时 myView2 从右边淡入成为当前可见视图。在动画结束的时候更新两个 hidden 属性,以便这两个视图正确显现。

代码 5-1 Animating a transition between two views in iOS

CATransition *transition = [CATransition animation];

transition.startProgress = 0;

transition.endProgress = 0;

transition.type = kCATransitionPush;

transition.subtype = kCATransitionFromRight;

transition.duration = 1.0;


// Add the transition animation to both layers

[myView1.layer addAnimation:transition forKey:@"transition"];

[myView2.layer addAnimation:transition forKey:@"transition"];


// Finally, change the visibility of the layers

myView1.hidden = YES;

myView2.hidden = NO;

当两个 layer 被包括进同一个过渡中,你可以为他们使用同一个过渡对象。使用同一个过渡对象简化了代码。然而,当每个 layer 的过渡的参数不同时,就需要使用多个过渡对象了。

代码 5-2 展示了在 OS X 上怎么使用 Core Image 滤镜实现过渡效果。用你想要的参数配置完滤镜后,把它赋给过渡对象的 filter 属性。之后的动画应用过程和其他动画对象的类型相同。

代码 5-2 Using a Core Image filter to animate a transition on OS X

// Create the Core Image filter, setting several key parameters.

CIFilter *aFilter = [CIFilter filterWithName:@"CIBarsSwipeTransition"];

[aFilter setValue:[NSNumber numberWithFloat:3.14] forKey:@"inputAngle"];

[aFilter setValue:[NSNumber numberWithFloat:30.0] forKey:@"inputWidth"];

[aFilter setValue:[NSNumber numberWithFloat:10.0] forKey:@"inputBarOffset"];


// Create the transition object

CATransition *transition = [CATranstion animation];

transition.startProgress = 0;

transition.endProgress = 1.0;

transition.filter = aFilter;

transition.duration = 1.0;


[self.imageView2 setHidden:NO];

[self.imageView.layer addAnimation:transition forKey:@"transition"];

[self.imageView2.layer addAnimation:transition forKey:@"transition"];

[self.imageView setHidden:YES];


注意:当在动画中使用 Core Image 滤镜时,最困难的部分是配置滤镜。例如,使用 bar swipe 过渡,确定一个 input angle 太高或者太低都可能会造成它看起来没有发生过渡。如果你没有看到期望的动画,尝试着调整滤镜的参数,查看参数值的变化十分产生变化的结果。


Customizing the Timing of an Animation

时间是动画中很重要的部分,通过 Core Animation 协议 CAMediaTiming 的方法和属性指明动画的时间精度信息。有两个 Core Animation 类使用这个协议。CAAnimation使用它,你可以通过这个类的实例指定时间信息。CALayer 也使用这个协议,尽管隐性的动画通常具有优先默认的时间信息,你也可以使用一些相关的时间属性配置它。

当考虑动画和时间时,理解 layer 对象怎么用时间工作很重要。每一个 layer 都有它自己用来管理动画计时的本地时间。一般两个不同的 layer 的本地时间是很接近的,你可以为他们使用同一个时间值而用户可能不会注意到。但是本地时间可以被它的父层 layer 或者自身的计时参数修改。比如,改变 layer 的 speed 属性会导致自身(子层 layer)动画的持续时间相应地变化。

为了协助你为给定的 CALayer 确定合适的时间值,CALayer 类定义了 convertTime:fromeLayer: 和 convertTime:toLayer: 方法。你可以用这些方法转换一个固定的时间值为 layer 的本地时间或者把一个 layer 的时间值转换为另一个的。这两个方法考虑了可能影响 layer 本地时间的媒体时间属性,而且返回一个你可以在其他 layer 使用的值。代码 5-3 展示了常规下获得一个 layer 的当前本地时间。CACurrentMediaTime 方法是一个很方便的方法,返回当前电脑的时钟时间,会被转换为 layer 的本地时间。

代码 5-3 Getting a layer's current local time

CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];

一旦你有了 layer 的本地时间值,你可以使用它更新动画对象或者 layer 时间相关的属性。使用这些时间属性,你可以获得一些有趣的动画行为,包括:

  • 使用 beginTime 属性设置动画的起始时间。一般地,动画在下一个循环更新期间开始。你可以使用 beginTime 参数延迟几秒动画的开始时间。把两个动画串一起的方式是设置一个的开始时间为另一个的结束时间。

    如果你延迟了动画的开始,你也可能想设置 fillMode 属性为 kCAFillModeBackwards。这个填充模式导致 layer 显示动画的开始值,即使在 layer 树中的 layer 对象包含一个不同的值。没有这个填充模式,你将在动画开始执行前看到一个跳跃的最终值。其他的填充模式也可以使用。
  • autoreverses 属性导致一个动画在明确的区间执行,然后返回动画的开始值。你可以用 repeatCount 属性合并这个属性,然后动画回来,在起始值之间跳转。为一个自动转变的动画设置重复记数为整数值(比如 1.0)会导致动画在开始值上停止。添加额外半步(比如 1.5)会导致动画在它的结束值停止。
  • 用组动画使用 timeOffset 属性比其他的动画要开始的晚一点。

Pausing and Resuming Animations

你可以利用 layer 实现 CAMediaTiming 协议,然后设置 layer 的动画速度为 0.0,会使动画暂停。设置速度为 0 会暂停动画直至你变为非零值。代码 5-4 展示了一个如何暂停和恢复动画的简单例子。

代码 5-4 Pausing and resuming a layer's animations

-(void)pauseLayer:(CALayer*)layer{
	CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
	
	layer.speed = 0.0;
	
	layer.timeOffset = pausedTime;
}


-(void) resumeLayer:(CALayer*)layer{
	CFTimeInterval pausedTime = [layer timeOffset];
	
	layer.speed = 1.0;
	
	layer.timeOffset = 0.0;
	
	layer.beginTime = 0.0;
	
	CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
	
	layer.beginTime = timeSincePause;
}


Explicit Transaction Let You Change Animation Parameters

你做的每一个改变必须是事务的一部分。CATransaction 类管理动画的创建和聚合,还有动画在合适时间的执行。多数情况下,你不需要创建自己的事务。无论你在什么时候为 layer 添加隐性或显性动画, Core Animation 自动地创建了一个隐性事务。但是你也可以创建自己的事务,更大粒度的管理这些动画。

你用 CATransaction 类的方法创建和管理事务。调用类方法 begin 开始(隐性创建)一个新的事务;调用类方法 commit 结束事务。在这两个方法调用中间是你想做的事务部分的改变。例如,改变 layer 的两个属性,你可以使用代码 5-5 中的代码。

代码 5-5 Creating an explicit transaction

[CATransaction begin];

theLayer.zPosition = 200.0;

theLayer.opacity = 0.0;

[CATransaction commit];

使用事务的最主要原因之一是因为在一个显式的事务限制之内,你可以改变持续时间,时间方法和其他的参数。你也可以赋给整个事务一个完整的 block,以便你的 app 在一组动画完成时收到通知。改变动画参数需要使用 setValue:forKey: 方法修改事务字典的合适键。例如,更改默认持续时间到 10 秒,你将改变 kCATransactionAnimationDuration 键,如代码 5-6

代码 5-6 Changing the default duration of animaitons

[CATransaction begin];

[CATransaction setValue:[NSNumber numberWithFloat:10.0f] forKey:kCATransactionAnimationDuration];

// Perform the animations

[CATransaction commit];

你可以在想为不同的动画集提供不同的默认值时叠加事务。把一个事务叠加到另一个中,只需要再一次调用类方法 begin。每一次 begin 的调用必须有一个相对应的 commit 方法调用。只有你为最外面的事务提交了变更 Core Animation 才开始相关的动画。

代码 5-7 展示了一个嵌套的事务。在这个例子中,内层的事务像外层一样改变了同一个动画的参数,但是使用了不同的值。

代码 5-7 Nesting explicit transactions

[CATransaction begin]; // Outer transaction


// Change the animation duration to two seconds

[CATransaction setValue:[NSNumber numberWithFloat:2.0f] forKey:kCATransactionAnimationDuration];

// Move the layer to a new position

theLayer.position = CGPointMake(0.0, 0.0);



[CATransaction begin]; // Inner transaction

// Change the animation duration to five seconds

[CATransaction setValue:[NSNumber numberWithFloat:5.0f] forKey:kCATransactionAnimationDuration];


// Change the zPosition and opacity

theLayer.zPosition = 200.0;

theLayer.opacity = 0.0;


[CATransaction commit]; // Inner transaction


[CATransaction commit]; // Outer transaction

Adding Perspective to Your Animations

App 可以在三维空间中操作 layer,但是为了简单 Core Animation 使用一个平行项目显示 layer,这个项目把场景平植到一个二维平面中。这个默认的行为导致相同尺寸不同 zPosition 值的 layer 以同一个尺寸展现,即使他们在 z 轴上相差很远。一般情况下你看到的三维场景透视就不存在啦。但是,你可以修改 layer 的 transformation matrix 包含透视信息来改变这个行为。

当修改场景的透视时,你需要修改包含了被看到的视图的父层 layer 的 sublayerTransform 矩阵。修改父层 layer 简化了你不得不写的应用到所有子层的透视信息的代码。它还确保了透视能被正确的应用到在不同平面上重叠的兄弟层 layr。

代码 5-8 展示了创建一个父层 layer 的简单透视 transform 代码。在这里自定义的 eyePosition 变量指明了沿着 z 轴到视图的相对距离。一般你为 eyePosition 指定一个正数,以便保持 layer 的水平。在一个平面视图中会有更大的值,而更小的值会导致不同的 layer 之间产生戏剧性的视觉效果。

代码 5-8 Adding a perspective transform to a parent layer

CATransform3D perspective = CATransform3DIdentity;

perspective.m34 = -1.0/eyePosition;


// Apply the transform to a parent layer.

myParentLayer.sublayerTransform = perspective;

使用父层 layer 的配置,你可以改变任意子层 layer 的 zPosition 属性,而且还可以观察它们的尺寸是如何基于它们的相对距离从视觉位置改变的。

posted @ 2015-02-03 18:11  1oo1  阅读(242)  评论(0编辑  收藏  举报