Core Animation - 9

Using a Keyframe Animation to Change Layer Properties

我们首先来了解一下Keyframe:

  • A key frame in animation and filmmaking is a drawing that defines the starting and ending points of any smooth transition.   From wiki.
  • 百度百科解释得也相当不错:计算机动画术语, 帧——就是动画中最小单位的单幅影像画面,相当于电影胶片上的每一格镜头。在动画软件的时间轴上帧表现为一格或一个标记。关键帧——相当于二维动画中的原画。指角色或者物体运动或变化中的关键动作所处的那一帧。关键帧与关键帧之间的动画可以由软件来创建,叫做过渡帧或者中间帧。
要了解关键帧动画,首先了解代表它的类:CAKeyframeAnimation :是继承于CAPropertyAnimation类
学习完这章,要起码地对它所有的属性有所了解和熟悉:
@interface CAKeyframeAnimation : CAPropertyAnimation

@property(copy) NSArray *values;

@property CGPathRef path;

@property(copy) NSArray *keyTimes;

@property(copy) NSArray *timingFunctions;

@property(copy) NSString *calculationMode;

@property(copy) NSArray *tensionValues;
@property(copy) NSArray *continuityValues;
@property(copy) NSArray *biasValues;

@property(copy) NSString *rotationMode;

@end

这里有一些例子:
values 和 calculationMode 的使用:
        CAKeyframeAnimation* widthAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderWidth"];
        NSArray* widthValues = [NSArray arrayWithObjects:@1.0, @10.0, @5.0,
                                                               @30.0, @0.5,
                                                               @15.0, @2.0,
                                                               @50.0, @0.0, nil];
        widthAnim.values = widthValues;
        widthAnim.calculationMode = kCAAnimationPaced;
        
        CAKeyframeAnimation* colorAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderColor"];
        NSArray* colorValues = [NSArray arrayWithObjects:(id)[UIColor greenColor].CGColor,
                                                         (id)[UIColor redColor].CGColor,
                                                         (id)[UIColor blueColor].CGColor, nil];
        colorValues = colorValues;
        colorAnim.calculationMode = kCAAnimationPaced;

path的使用:
        CGMutablePathRef thePath = CGPathCreateMutable();
        CGPathMoveToPoint(thePath, NULL, 74.0, 74.0);
        
        CGPathAddCurveToPoint(thePath, NULL, 74.0, 500.0,
                              320.0, 500.0,
                              320.0, 74.0);
        
        CGPathAddCurveToPoint(thePath, NULL, 320.0, 500.0,
                                                    566.0, 500.0,
                                                    566.0, 74.0);

        CAKeyframeAnimation* theAnimation;
        theAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        theAnimation.path = thePath;
        theAnimation.duration = 5.0;
        
        [layer addAnimation:theAnimation forKey:@"position"];







Whereas a property-based animation changes a property from a start value to an end value, aCAKeyframeAnimation object lets you animate through a set of target values in a way that might or might not be linear. A key frame animation consists of a set of target data values and the times at which each value should be reached. In the simplest configuration, you specify both the values and times using an array. For changes to a layer’s position, you can also have the changes follow a path. The animation object takes the key frames you specify and builds the animation by interpolating from one value to the next over the given time periods.

CAKeyframeAnimation让你的应用能够按从一系列目标值线性地或非线性地变化。一个关键帧动画包含着一系列目标数据值and the times,每一个值应该被达成的。在最简单的配置中,你确定所有值用一个数组。为了改变图层的position,你也可以修改path。 动画对象使用你所定义的关键帧来创建动画操作:通过每时刻的下一步插入每一个值在所给定的时期里。
如图的路径效果是通过使用CGPathRef来实现的,使得它的position随着路径运动,代码实现如下:
// create a CGPath that implements two arcs (a bounce)
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,74.0,74.0);
CGPathAddCurveToPoint(thePath,NULL,74.0,500.0,
                                   320.0,500.0,
                                   320.0,74.0);
CGPathAddCurveToPoint(thePath,NULL,320.0,500.0,
                                   566.0,500.0,
                                   566.0,74.0);
 
CAKeyframeAnimation * theAnimation;
 
// Create the animation object, specifying the position property as the key path.
theAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
theAnimation.path=thePath;
theAnimation.duration=5.0;
 
// Add the animation to the layer.
[theLayer addAnimation:theAnimation forKey:@"position"];

官方文档这个例子还是模糊,就拿上一篇中的例子Demo来,将这段代码添加至:因为屏幕大小原因,将第二段路径Curve先注释掉:
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        LBLayer* layer = [[LBLayer alloc] init];
        layer.bounds = CGRectMake(0, 0, 185, 185);
        layer.position = CGPointMake(160, 284);
        layer.backgroundColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0].CGColor;
     
        [layer setNeedsDisplay];
        
        [self.layer addSublayer:layer];
        
        //设置好路径先
        CGMutablePathRef thePath = CGPathCreateMutable();
        CGPathMoveToPoint(thePath, NULL, 74.0, 74.0);
        
        CGPathAddCurveToPoint(thePath, NULL, 74.0, 500.0,
                              320.0, 500.0,
                              320.0, 74.0);
        
        //        CGPathAddCurveToPoint(thePath, NULL, 320.0, 500.0,
        //                                             566.0, 500.0,
        //                                             566.0, 74.0);
        
        CAKeyframeAnimation* theAnimation;
        theAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        theAnimation.path = thePath;
        theAnimation.duration = 5.0;
        
        [layer addAnimation:theAnimation forKey:@"position"];
        
    }
    
    return self;
}

结果很显然,和文档给的演示一样,在运动完这一段曲线运动后,马上回到原点。 关于添加这个animation object code 的位置,经过测试,在[ self.layer addSublayer:layer ]前后添加效果都能完成。
这段代码:
theAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

[layer addAnimation:theAnimation forKey:@"position"];

都反映了KVC的使用。




Specifying Keyframe Values

The key frame values are the most important part of a keyframe animation.These values define the behavior of the animation over the course of its execution. The main way to specify keyframe values is as an array of objects but for values that contain a CGPoint data type (such as the layer’s anchorPoint and position properties), you can specify a CGPathRef data type instead.

When specifying an array of values, what you put into the array depends on the data type required by the property. You can add some objects to an array directly; however,some objects must be cast to id before being added, and all scalar types or structs must be wrapped by an object. For example:

  • For properties that take a CGRect (such as the bounds and frame properties), wrap each rectangle in anNSValue object.
  • For the layer’s transform property, wrap each CATransform3D/a matrix in an NSValue object. Animating this property causes the keyframe animation to apply each transform matrix to the layer in turn.
  • For the borderColor property, cast each CGColorRef data type to the typeid before adding it to the array.
  • For properties that take a CGFloat value, wrap each value in an NSNumber object before adding it to the array.
  • When animating the layer’s contents property, specify an array of CGImageRef data types.
对关键帧取值所应该做的基本数据处理:要几个好的例子才能充分弄懂啊!

For properties that take aCGPoint data type, you can create an array of points (wrapped inNSValue objects) or you can use aCGPathRef object to specify the path to follow. When you specify an array of points, the keyframe animation object draws a straight line between each successive point and follows that path. When you specify aCGPathRef object, the animation starts at the beginning point of the path and follows its outline, including along any curved surfaces. You can use either an open or closed path.

CGFlot 要被wrap成NSNumber : CGFloat --> NSNumber

        NSArray* widthValues = [NSArray arrayWithObjects:@1.0, @10.0, @5.0,
                                                               @30.0, @0.5,
                                                               @15.0, @2.0,
                                                               @50.0, @0.0, nil];
        widthAnim.values = widthValues;

 CGColor要被wrap成id类型 : UIColor --> CGColor --> id

        NSArray* colorValues = [NSArray arrayWithObjects:(id)[UIColor greenColor].CGColor,
                                                         (id)[UIColor redColor].CGColor,
                                                         (id)[UIColor blueColor].CGColor, nil];



Specifying the Timing of a Keyframe Animation

The timing and pacing of keyframe animations is more complex than those of basic animations and there are several properties you can use to control it:

  • The calculationMode property defines the algorithm to use in calculating the animation timing. The value of this property affects how the other timing-related properties are used.
    • Linear and cubic animations—that is, animations where thecalculationMode property is set to kCAAnimationLine or kCAAnimationCubic — use the provided timing information to generate the animation. These modes give you the maximum control over the animation timing.
    • Paced animations—that is, animations where thecalculationMode property is set to kCAAnimationPaced or kCAAnimationCubicPace —do not rely on the external timing values provided by thekeyTimes or timingFunctions properties. Instead, timing values are calculated implicitly to provide the animation with a constant velocity.
    • Discrete animations—that is, animations where thecalculationMode property is set to kCAAnimationDiscrete —cause the animated property to jump from one keyframe value to the next without any interpolation. This calculation mode uses the values in thekeyTimes property but ignores thetimingFunctions property
  • The keyTimes property specifies time markers at which to apply each keyframe value. This property is used only if the calculation mode is set to kCAAnimationLinear, kCAAnimationDiscrete, or kCAAnimationCubic.  It is not used for paced animations.
  • The timeFunctions property specifies the timing curves to use for each keyframe segment. (This property replaces the inherited timingFunctions property.)
If you want to handle the animation timing yourself, use the kCAAnimationLinear or kCAAnimationCubic mode and the keyTimes and timingFunctions properties. The keyTimes defines the points in time at which to apply each keyframe value. The timing for all intermediate values is controlled by the timing functions, which allow you to apply ease-in or ease-out curves to each segment. If you do not specify any timing functions, the timing is linear.

看完这个也不是很理解其中的Timing的概念和使用,日后补充。




Stopping an Explicit Animation While It Is Running

Animations normally run until they are complete, but you can stop them early if needed using one of the following techniques:

  • To remove a single animation object from the layer, call the layer’s removeAnimationForKey: method to remove your animation object. This method uses the key that was passed to the addAnimaiton:forKey: method to identify the animation. The key you specify must not be nil.
  • To remove all animation objects from the layer, call the layer’s removeAllAnimations method. This method removes all ongoing animations immediately and redraws the layer using its current state information.
可以通过这些方法来移除显式动画。

When you remove an animation from a layer, Core Animation responds by redrawing the layer using its current values. Because the current values are usually the end values of the animation, this can cause the appearance of the layer to jump suddenly. If you want the layer’s appearance to remain where it was on the last frame of the animation,you can use the objects in the presentation tree to retrieve those final values and set them on the objects in the layer tree.

因为移除显式动画Core Animation的操作是使用当前值来重绘图层,从而造成appearance的瞬息改变,所以你可以使用presentation tree来检索最终值并且将它们重设在图层继承树上。




Animating Multiple Changes Together

If you want to apply multiple animations to a layer object simultaneously, you can group them together using a CAAnimationGroup object. Using a group object simplifies the management of multiple animation objects by providing a single configuration point. Timing and duration values applied to the group override those same values in the individual animation objects.

// Animation 1
CAKeyframeAnimation* widthAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderWidth"];
NSArray* widthValues = [NSArray arrayWithObjects:@1.0, @10.0, @5.0, @30.0, @0.5, @15.0, @2.0, @50.0, @0.0, nil];
widthAnim.values = widthValues;
widthAnim.calculationMode = kCAAnimationPaced;
 
// Animation 2
CAKeyframeAnimation* colorAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderColor"];
NSArray* colorValues = [NSArray arrayWithObjects:(id)[UIColor greenColor].CGColor,
            (id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor,  nil];
colorAnim.values = colorValues;
colorAnim.calculationMode = kCAAnimationPaced;
 
// Animation group
CAAnimationGroup* group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:colorAnim, widthAnim, nil];
group.duration = 5.0;
 
[myLayer addAnimation:group forKey:@"BorderChanges"];

还是一样在前面的例子上添加:

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        LBLayer* layer = [[LBLayer alloc] init];
        //注意这里,一个普通的图层只要bounds,position,backgroundColor注意到了就可以,并且都是可动画的
        layer.bounds = CGRectMake(0, 0, 185, 185);
        layer.position = CGPointMake(160, 284);
        layer.backgroundColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0].CGColor;


        [layer setNeedsDisplay];
        
        [self.layer addSublayer:layer];
        
        CAKeyframeAnimation* widthAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderWidth"];
        NSArray* widthValues = [NSArray arrayWithObjects:@1.0, @10.0, @5.0,
                                                               @30.0, @0.5,
                                                               @15.0, @2.0,
                                                               @50.0, @0.0, nil];
        widthAnim.values = widthValues;
        widthAnim.calculationMode = kCAAnimationPaced;
        
        CAKeyframeAnimation* colorAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderColor"];
        NSArray* colorValues = [NSArray arrayWithObjects:(id)[UIColor greenColor].CGColor,
                                                         (id)[UIColor redColor].CGColor,
                                                         (id)[UIColor blueColor].CGColor, nil];
        colorValues = colorValues;
        colorAnim.calculationMode = kCAAnimationPaced;
        
        CAAnimationGroup* group = [CAAnimationGroup animation];
        group.animations = [NSArray arrayWithObjects:colorAnim, widthAnim, nil];
        group.duration = 5.0;
        
        [layer addAnimation:group forKey:@"BorderChanges"];
        
    }
    
    return self;
}
结果是非常炫酷的。

A more advanced way to group animations together is to use atransaction object. Transactions provide more flexibility by allowing you to create nested sets of animations and assign different animation parameters for each. 

一个更加先进的方式来组合动画操作是使用: transaction 对象。Transactions 提供了更加复杂通过允许你创建嵌套的动画集合并且分配不同的动画参数。



Detecting the End of an Animation

Core Animation provides support fordetecting when an animation begins or ends. These notificationsare a good time to do any housekeeping tasks associated with the animation. For example, you might use a start notification to set up some related state information and use the corresponding end notification to tear down that state.

There are two different ways to be notified about the state of an animation:

  • Add a completion block to the current transaction using thesetCompletionBlock: method. When all of the animations in the transaction finish, the transaction executes your completion block.
  • Assign a delegate to your CAAnimation object and implement theanimationDidStart: and animationDidStop:finished: delegate methods.
两种方法来达到时间点上对动画的detect, 一个是使用completion block , 另外一个是使用代理delegate的方法: animationDidStart   animationDidStop:finished

If you want to chain two animations together so that one starts when the other finishes, do not use animation notifications. Instead, use the beginTime property of your animation objects to start each one at the desired time. To chain two animations together, set the start time of the second animation to the end time of the first animation. 

但是如果你想要连接两段动画,不要用上面的方式,应该设置beginTime属性来完成。




How to Animate Layer-Backed Views

If a layer belongs to a layer-backed view, the recommended way to create animations is to use the view-based animation interfaces provided by UIKit or AppKit. There are ways to animate the layer directly using Core Animation interfaces but how you create those animations depends on the target platform.

上面提到的都是Core Animation直接对layer 的使用,然而如果是Layer-backed view的动画使用呢? iOS是建议直接用UIKit和AppKit的 view-based 动画接口来创建。 


Rules for Modifying Layers in iOS

Because iOS views always have an underlying layer, the UIView class itself derives most of its data from the layer object directly. As a result, changes you make to the layer are automatically reflected by the view object as well. This behavior means that you can use either the Core Animation orUIView interfaces to make your changes.

If you want to use Core Animation classes to initiate animations, you must issue all of your Core Animation calls from inside a view-based animation block. The  UIView class disables layer animations by default but reenables them inside animation blocks.So any changes you make outside of an animation block are not animated.Listing 3-5 shows an example of how to change a layer’s opacity implicitly and its position explicitly. In this example, themyNewPosition variable is calculated beforehand and captured by the block. Both animations start at the same time but the opacity animation runs with the default timing while the position animation runs with the timing specified in its animation object.

iOS视图往往原生性地含有底层图层对象,UIView类本身直接派生大多数它的数据从它的层对象。因此,修改图层的改变将自动地反应在视图view本身。这个行为意味着你可以使用Core Animation 或者 是UIView接口来创建你的改变。

还有提到的animation block.

如例子所示:

[UIView animateWithDuration:1.0 animations:^{
   // Change the opacity implicitly.
   myView.layer.opacity = 0.0;
 
   // Change the position explicitly.
   CABasicAnimation* theAnim = [CABasicAnimation animationWithKeyPath:@"position"];
   theAnim.fromValue = [NSValue valueWithCGPoint:myView.layer.position];
   theAnim.toValue = [NSValue valueWithCGPoint:myNewPosition];
   theAnim.duration = 3.0;
   [myView.layer addAnimation:theAnim forKey:@"AnimateFrame"];
}];
恍然大悟,在UIView上使用Core Animation原来可以如此!

Remember to Update View Constraints as Part of Your Animation

If you are using constraint-based layout rules to manage the position of your views, you must remove any constraints that might interfere with an animation as part of configuring that animation. Constraints affect any changes you make to the position or size of a view. They also affect the relationships between the view and its child views. If you are animating changes to any of those items, you can remove the constraints, make the change, and then apply whatever new constraints are needed.





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值