动画其实就是一段时间内发生的改变,最简单的形式就是从一个值改变到另一个值,这也是CABasicAnimation
最主要的功能。CABasicAnimation
是CAPropertyAnimation
的一个子类,而CAPropertyAnimation
的父类是CAAnimation
,CAAnimation
同时也是Core Animation
所有动画类型的抽象基类。作为一个抽象类,CAAnimation
本身并没有做多少工作,它提供了一个计时函数(见第十章“缓冲”),一个委托(用于反馈动画状态)以及一个removedOnCompletion
,用于标识动画是否该在结束后自动释放(默认YES
,为了防止内存泄露)。CAAnimation
同时实现了一些协议,包括CAAction
(允许CAAnimation
的子类可以提供图层行为),以及CAMediaTiming
(第九章“图层时间”将会详细解释)。
CAPropertyAnimation
通过指定动画的keyPath
作用于一个单一属性,CAAnimation
通常应用于一个指定的CALayer
,于是这里指的也就是一个图层的keyPath
了。实际上它是一个关键路径(一些用点表示法可以在层级关系中指向任意嵌套的对象),而不仅仅是一个属性的名称,因为这意味着动画不仅可以作用于图层本身的属性,而且还包含了它的子成员的属性,甚至是一些虚拟的属性(后面会详细解释)。
CABasicAnimation
继承于CAPropertyAnimation
,并添加了如下属性:
id fromValue
id toValue
id byValue
从命名就可以得到很好的解释:fromValue
代表了动画开始之前属性的值,toValue
代表了动画结束之后的值,byValue
代表了动画执行过程中改变的值。
通过组合这三个属性就可以有很多种方式来指定一个动画的过程。它们被定义成id
类型而不是一些具体的类型是因为属性动画可以用作很多不同种的属性类型,包括数字类型,矢量,变换矩阵,甚至是颜色或者图片。
id
类型可以包含任意由NSObject
派生的对象,但有时候你会希望对一些不直接从NSObject
继承的属性类型做动画,这意味着你需要把这些值用一个对象来封装,或者强转成一个对象,就像某些功能和Objective-C
对象类似的Core Foundation
类型。但是如何从一个具体的数据类型转换成id看起来并不明显,一些普通的例子见表8.1。
CAAnimationDelegate
在第七章使用隐式动画的时候,我们可以在CATransaction
完成块中检测到动画的完成。但是这种方式并不适用于显式动画,因为这里的动画和事务并没太多关联。
那么为了知道一个显式动画在何时结束,我们需要使用一个实现了CAAnimationDelegate
协议的delegate
。
CAAnimationDelegate
在任何头文件中都找不到,但是可以在CAAnimation
头文件或者苹果开发者文档中找到相关函数。在这个例子中,我们用-animationDidStop:finished:
方法在动画结束之后来更新图层的backgroundColor
。
当更新属性的时候,我们需要设置一个新的事务,并且禁用图层行为。否则动画会发生两次,一个是因为显式的CABasicAnimation
,另一次是因为隐式动画,具体实现见订单8.3。
清单8.3 动画完成之后修改图层的背景色
@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 it to our view
[self.layerView.layer addSublayer:self.colorLayer];
}
- (IBAction)changeColor
{
//create a new random color
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
//create a basic animation
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"backgroundColor";
animation.toValue = (__bridge id)color.CGColor;
animation.delegate = self;
//apply animation to layer
[self.colorLayer addAnimation:animation forKey:nil];
}
- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag
{
//set the backgroundColor property to match animation toValue
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;
[CATransaction commit];
}
@end
属性动画
CABasicAnimation *animation = [CABasicAnimation animation];
[self updateHandsAnimated:NO];
animation.keyPath = @"transform";
animation.toValue = [NSValue valueWithCATransform3D:transform];
animation.duration = 0.5;
animation.delegate = self;
[animation setValue:handView forKey:@"handView"];
关键帧动画
CAKeyframeAnimation
是另一种UIKit
没有暴露出来但功能强大的类。和CABasicAnimation
类似,``CAKeyframeAnimation同样是CAPropertyAnimation
的一个子类,它依然作用于单一的一个属性,但是和CABasicAnimation
不一样的是,它不限制于设置一个起始和结束的值,而是可以根据一连串随意的值来做动画。
关键帧起源于传动动画,意思是指主导的动画在显著改变发生时重绘当前帧(也就是关键帧),每帧之间剩下的绘制(可以通过关键帧推算出)将由熟练的艺术家来完成。CAKeyframeAnimation
也是同样的道理:你提供了显著的帧,然后Core Animation
在每帧之间进行插入。
- (IBAction)changeColor
{
//create a keyframe animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"backgroundColor";
animation.duration = 2.0;
animation.values = @[
(__bridge id)[UIColor blueColor].CGColor,
(__bridge id)[UIColor redColor].CGColor,
(__bridge id)[UIColor greenColor].CGColor,
(__bridge id)[UIColor blueColor].CGColor ];
//apply animation to layer
[self.colorLayer addAnimation:animation forKey:nil];
}
动画组
CABasicAnimation
和CAKeyframeAnimation
仅仅作用于单独的属性,而CAAnimationGroup
可以把这些动画组合在一起。CAAnimationGroup
是另一个继承于CAAnimation
的子类,它添加了一个animations
数组的属性,用来组合别的动画。
- (void)viewDidLoad
{
[super viewDidLoad];
//create a path
UIBezierPath *bezierPath = [[UIBezierPath alloc] init];
[bezierPath moveToPoint:CGPointMake(0, 150)];
[bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(75, 0) controlPoint2:CGPointMake(225, 300)];
//draw the path using a CAShapeLayer
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.path = bezierPath.CGPath;
pathLayer.fillColor = [UIColor clearColor].CGColor;
pathLayer.strokeColor = [UIColor redColor].CGColor;
pathLayer.lineWidth = 3.0f;
[self.containerView.layer addSublayer:pathLayer];
//add a colored layer
CALayer *colorLayer = [CALayer layer];
colorLayer.frame = CGRectMake(0, 0, 64, 64);
colorLayer.position = CGPointMake(0, 150);
colorLayer.backgroundColor = [UIColor greenColor].CGColor;
[self.containerView.layer addSublayer:colorLayer];
//create the position animation
CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animation];
animation1.keyPath = @"position";
animation1.path = bezierPath.CGPath;
animation1.rotationMode = kCAAnimationRotateAuto;
//create the color animation
CABasicAnimation *animation2 = [CABasicAnimation animation];
animation2.keyPath = @"backgroundColor";
animation2.toValue = (__bridge id)[UIColor redColor].CGColor;
//create group animation
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = @[animation1, animation2];
groupAnimation.duration = 4.0;
//add the animation to the color layer
[colorLayer addAnimation:groupAnimation forKey:nil];
}
过渡
有时候对于iOS应用程序来说,希望能通过属性动画来对比较难做动画的布局进行一些改变。比如交换一段文本和图片,或者用一段网格视图来替换,等等。属性动画只对图层的可动画属性起作用,所以如果要改变一个不能动画的属性(比如图片),或者从层级关系中添加或者移除图层,属性动画将不起作用。
于是就有了过渡的概念。过渡并不像属性动画那样平滑地在两个值之间做动画,而是影响到整个图层的变化。过渡动画首 先展示之前的图层外观,然后通过一个交换过渡到新的外观。
为了创建一个过渡动画,我们将使用CATransition
,同样是另一个CAAnimation
的子类,和别的子类不同,CATransition
有一个type
和subtype
来标识变换效果。type
属性是一个NSString
类型,可以被设置成如下类型:
kCATransitionFade
kCATransitionMoveIn
kCATransitionPush
kCATransitionReveal
到目前为止你只能使用上述四种类型,但你可以通过一些别的方法来自定义过渡效果,默认的过渡类型是kCATransitionFade
,当你在改变图层属性之后,就创建了一个平滑的淡入淡出效果。kCATransitionPush
,它创建了一个新的图层,从边缘的一侧滑动进来,把旧图层从另一侧推出去的效果。
kCATransitionMoveIn
和kCATransitionReveal
与kCATransitionPush
类似,都实现了一个定向滑动的动画,但是有一些细微的不同,kCATransitionMoveIn
从顶部滑动进入,但不像推送动画那样把老土层推走,然而kCATransitionReveal
把原始的图层滑动出去来显示新的外观,而不是把新的图层滑动进入。
后面三种过渡类型都有一个默认的动画方向,它们都从左侧滑入,但是你可以通过subtype来控制它们的方向,提供了如下四种类型:
kCATransitionFromRight
kCATransitionFromLeft
kCATransitionFromTop
kCATransitionFromBottom
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIImageView *imageView;
@property (nonatomic, copy) NSArray *images;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//set up images
self.images = @[[UIImage imageNamed:@"Anchor.png"],
[UIImage imageNamed:@"Cone.png"],
[UIImage imageNamed:@"Igloo.png"],
[UIImage imageNamed:@"Spaceship.png"]];
}
- (IBAction)switchImage
{
//set up crossfade transition
CATransition *transition = [CATransition animation];
transition.type = kCATransitionFade;
//apply transition to imageview backing layer
[self.imageView.layer addAnimation:transition forKey:nil];
//cycle to next image
UIImage *currentImage = self.imageView.image;
NSUInteger index = [self.images indexOfObject:currentImage];
index = (index + 1) % [self.images count];
self.imageView.image = self.images[index];
}
@end
你可以从代码中看出,过渡动画和之前的属性动画或者动画组添加到图层上的方式一致,都是通过-addAnimation:forKey:
方法。但是和属性动画不同的是,对指定的图层一次只能使用一次CATransition
,因此,无论你对动画的键设置什么值,过渡动画都会对它的键设置成“transition”,也就是常量kCATransition
。
自定义动画
果通过UIView +transitionFromView:toView:duration:options:completion:
和+transitionWithView:duration:options:animations:
方法提供了Core Animation
的过渡特性。但是这里的可用的过渡选项和CATransition
的type
属性提供的常量完全不同。UIView
过渡方法中options
参数可以由如下常量指定:
UIViewAnimationOptionTransitionFlipFromLeft
使用UIKit
提供的方法来做过渡动画
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIImageView *imageView;
@property (nonatomic, copy) NSArray *images;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad]; //set up images
self.images = @[[UIImage imageNamed:@"Anchor.png"],
[UIImage imageNamed:@"Cone.png"],
[UIImage imageNamed:@"Igloo.png"],
[UIImage imageNamed:@"Spaceship.png"]];
- (IBAction)switchImage
{
[UIView transitionWithView:self.imageView duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
//cycle to next image
UIImage *currentImage = self.imageView.image;
NSUInteger index = [self.images indexOfObject:currentImage];
index = (index + 1) % [self.images count];
self.imageView.image = self.images[index];
}
completion:NULL];
}
@end
https://blog.csdn.net/qq2899926939/article/details/90453571 参考链接
网友评论