动画
1.隐式动画。没有制定任何动画的类型叫做隐式动画。事务,是通过CATransaction类来做管理,只能通过+begin和+commit分别来入栈河出栈。任何考验做动画的图层属性都会被添加到栈顶到事务。
CoreAnimation在每个runloop周期中自动开始一次新的事务,任何在一次runloop中属性的改变都会被集中起来,然后做一次0.25秒的动画。
[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
[CATransaction comit];
UIView的隐式动画:+animateWithDuration:animations: 和 animateWithDuration: animations: completion 可以带入Block便捷使用
2.我们把改变属性时CALayer自动应用的动画称作行为,当CALayer的属性被修改时候,它会调用-actionForKey:方法,传递属性的名称。
图层首先检测它是否有委托,并且是否实现CALayerDelegate协议指定的-actionForLayer:forKey方法。如果有,直接调用并返回结果。
如果没有委托,或者委托没有实现-actionForLayer:forKey方法,图层接着检查包含属性名称对应行为映射的actions字典。
如果actions字典没有包含对应的属性,那么图层接着在它的style字典接着搜索属性名。
最后,如果在style里面也找不到对应的行为,那么图层将会直接调用定义了每个属性的标准行为的-defaultActionForKey:方法。
所以一轮完整的搜索结束之后,-actionForKey:要么返回空(这种情况下将不会有动画发生),要么是CAAction协议对应的对象,最后CALayer拿这个结果去对先前和当前的值做动画。
3.UIKit是如何禁用隐式动画的:每个UIView对它关联的图层都扮演了一个委托,并且提供了-actionForLayer:forKey的实现方法。当不再一个动画块的实现中,UIView对所有图层行为返回nil,但是在动画block内,它返回一个非空值。
4.过渡
过渡并不像属性动画那样平滑地在两个值之间做动画,而是影响到整个图层的变化。过渡动画首先展示之前的图层外观,然后通过一个交换过渡到新的外观。
- (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];
}
UIView +transitionFromView:toView:duration:options:completion:和+transitionWithView:duration:options:animations:方法提供了Core Animation的过渡特性
UIViewAnimationOptionTransitionFlipFromRight UIViewAnimationOptionTransitionCurlUp UIViewAnimationOptionTransitionCurlDown UIViewAnimationOptionTransitionCrossDissolve UIViewAnimationOptionTransitionFlipFromTop UIViewAnimationOptionTransitionFlipFromBottom
我们对当前视图状态截图,然后在我们改变原始视图的背景色的时候对截图快速转动并且淡出,图8.5展示了我们自定义的过渡效果。
@implementation ViewController
- (IBAction)performTransition
{
//preserve the current view snapshot
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext();
//insert snapshot view in front of this one
UIView *coverView = [[UIImageView alloc] initWithImage:coverImage];
coverView.frame = self.view.bounds;
[self.view addSubview:coverView];
//update the view (we'll simply randomize the layer background color)
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.view.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
//perform animation (anything you like)
[UIView animateWithDuration:1.0 animations:^{
//scale, rotate and fade the view
CGAffineTransform transform = CGAffineTransformMakeScale(0.01, 0.01);
transform = CGAffineTransformRotate(transform, M_PI_2);
coverView.transform = transform;
coverView.alpha = 0.0;
} completion:^(BOOL finished) {
//remove the cover view now we're finished with it
[coverView removeFromSuperview];
}];
}
@end
一般说来,动画在结束之后被自动移除,除非设置removedOnCompletion为NO,如果你设置动画在结束之后不被自动移除,那么当它不需要的时候你要手动移除它;否则它会一直存在于内存中,直到图层被销毁。
5.显式动画
CATransform3D transform = CATransform3DMakeRotation(angle,0,0,1);
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keypath = @"transform";
animation.toValue = [NSValue valueWithCATransform3D:transform];
animation.duration = 0.5;
animation.delegate = self;
[animation setValue:handle forKey:@"handleView"];
[handView.layer addAnimation:animation forKey:nil];
CABasicAnimation有一个特别的性能:它的KVC可以像一个字典一样,可以用-setValue:forKey:和-valueForKey:来随意设置键值对,即使和你使用的动画类所声明的属性不匹配。
6.关键帧
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,
];
[self.colorLayer addAnimation:animation forKey:nil];
CAKeyframeAnimation有另一种方式去指定动画,就是使用CGPath。path属性可以用一种直观的方式,使用Core Graphics函数定义运动序列来绘制动画。
7.给CAKeyFrameAnimation添加了一个rotationMode的属性。设置它为常量kCAAnimationRotateAuto(清单8.7),图层将会根据曲线的切线自动旋转(图8.2)
8.虚拟属性
可以对transform.rotation关键路径应用动画,而不是transform本身
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform.rotation";
animation.duration = 2.0;
animation.byValue = @(M_PI * 2);
[shipLayer addAnimation:animation forKey:nil];
结果运行的特别好,用transform.rotation而不是transform做动画的好处如下:
我们可以不通过关键帧一步旋转多于180度的动画。
可以用相对值而不是绝对值旋转(设置byValue而不是toValue)。
可以不用创建CATransform3D,而是使用一个简单的数值来指定角度。
不会和transform.position或者transform.scale冲突(同样是使用关键路径来做独立的动画属性)。
Core Animation自动地根据通过CAValueFunction来计算的值来更新transform属性。