//CABasicAnimation
//[self basicAnimationTest];
//animationDelegate
//[self animationDelegateTest];
//clockViewAnimationTest
//[self clockViewAnimationTest];
//keyframeAnimation
//[self keyframeAnimationTest];
//CGPath
//[self keyframeAnimationPathTest];
//虚拟属性
//[self valueRotationTest];
//[self virtualValueRotationTest];
//动画数组
//[self animationGroupTest];
//过渡
//[self transitionTest];
//自定义动画
//[self transitionSelfTest];
//截图
//[self performTransition];
//取消动画
//[self cancelAnimationTest];
//duration repeatCount
//[self durationAndRepeatCountTest];
//repeatDuration
//[self repeatDurationTest];
//timeOffset and speed
//[self timeOffsetAndSpeedTest];
//handle animation
//[self handleAnimationTest];
属性动画
//属性动画:作用于图层的某个单一属性,并指定了它的一个目标值,或者一连串将要做动画的值。 分为两种:基础和关键帧
基础动画
//CABasicAnimation是CAPropertyAnimation的一个子类,而CAPropertyAnimation的父类是CAAnimation,CAAnimation同时也是Core Animation所有动画类型的抽象基类。
//CAAnimation提供了一个计时函数,一个委托(用于反馈动画状态)以及一个removedOnCompletion,用于标识动画是否该在结束后自动释放(默认YES,为了防止内存泄露)。CAAnimation同时实现了一些协议,包括CAAction(允许CAAnimation的子类可以提供图层行为),以及CAMediaTiming
//CAPropertyAnimation : keyPath 仅可以作用于图层本身的属性,而且还包含了它的子成员的属性,甚至是一些虚拟的属性
//CABasicAnimation继承于CAPropertyAnimation,add property id fromValue id toValue id byValue
//fromValue代表了动画开始之前属性的值,toValue代表了动画结束之后的值,byValue代表了动画执行过程中改变的值。 只需要指定toValue或者byValue
//Type Object Type Code Example
//CGFloat NSNumber id obj = @(float);
//CGPoint NSValue id obj = [NSValue valueWithCGPoint:point);
//CGSize NSValue id obj = [NSValue valueWithCGSize:size);
//CGRect NSValue id obj = [NSValue valueWithCGRect:rect);
//CATransform3D NSValue id obj = [NSValue valueWithCATransform3D:transform);
//CGImageRef id id obj = (__bridge id)imageRef;
//CGColorRef id id obj = (__bridge id)colorRef;
- (void)basicAnimationTest {
layerView = [[UIView alloc] init];
layerView.frame = CGRectMake(0, 20, 320, 320);
[self.view addSubview:layerView];
//add change color button
UIButton *changeColorBtn = [UIButton buttonWithType:UIButtonTypeCustom];
changeColorBtn.frame = CGRectMake(50, 200, 200, 44.0);
[changeColorBtn setTitle:@"change Color" forState:(UIControlStateNormal)];
[changeColorBtn addTarget:self action:@selector(changeColorBasicAnimationAction2) forControlEvents:UIControlEventTouchUpInside];
changeColorBtn.backgroundColor = [UIColor redColor];
[layerView addSubview:changeColorBtn];
//create sublayer
colorLayer = [CALayer layer];
colorLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0f);
colorLayer.backgroundColor = [UIColor blueColor].CGColor;
//add it to our view
[layerView.layer addSublayer:colorLayer];
}
- (void)changeColorBasicAnimationAction {
//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";
//因为动画并没有改变图层的模型,而只是呈现, 一旦动画结束并从图层上移除之后,图层就立刻恢复到之前定义的外观状态 从没改变过backgroundColor属性,所以图层就返回到原始的颜色
//在使用隐式动画的时候,实际上它就是用例子中CABasicAnimation来实现的(回忆第七章,我们在-actionForLayer:forKey:委托方法打印出来的结果就是CABasicAnimation)。
//animation.fromValue = (__bridge id)colorLayer.backgroundColor;
//colorLayer.backgroundColor = color.CGColor;
//由于这里的图层并不是UIView关联的图层,我们需要用CATransaction来禁用隐式动画行为,否则默认的图层行为会干扰我们的显式动画
CALayer *layer = colorLayer.presentationLayer ?: colorLayer;
animation.fromValue = (__bridge id)layer.backgroundColor;
[CATransaction begin];
[CATransaction setDisableActions:YES];
colorLayer.backgroundColor = color.CGColor;
[CATransaction commit];
animation.toValue = (__bridge id)color.CGColor;
//apply animation to layer
[colorLayer addAnimation:animation forKey:nil];
}
//避免在每次动画时候都重复CATransaction的代码。
- (void)applyBasicAnimation:(CABasicAnimation *)animation toLayer:(CALayer *)layer {
//set the from value (using presentation layer if available)
animation.fromValue = [layer.presentationLayer ?: layer valueForKeyPath:animation.keyPath];
//update the property in advance
//note: this approach will only work if toValue != nil
[CATransaction begin];
[CATransaction setDisableActions:YES];
[layer setValue:animation.toValue forKey:animation.keyPath];
[CATransaction commit];
//apply animation to layer
[layer addAnimation:animation forKey:nil];
}
- (void)changeColorBasicAnimationAction2 {
//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;
//apply animation without snap-back
[self applyBasicAnimation:animation toLayer:colorLayer];
}
//CAAnimationDelegate
- (void)animationDelegateTest {
layerView = [[UIView alloc] init];
layerView.frame = CGRectMake(0, 20, 320, 320);
[self.view addSubview:layerView];
//add change color button
UIButton *changeColorBtn = [UIButton buttonWithType:UIButtonTypeCustom];
changeColorBtn.frame = CGRectMake(50, 200, 200, 44.0);
[changeColorBtn setTitle:@"change Color" forState:(UIControlStateNormal)];
[changeColorBtn addTarget:self action:@selector(changeColorAnimationDelegateAction) forControlEvents:UIControlEventTouchUpInside];
changeColorBtn.backgroundColor = [UIColor redColor];
[layerView addSubview:changeColorBtn];
//create sublayer
colorLayer = [CALayer layer];
colorLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0f);
colorLayer.backgroundColor = [UIColor blueColor].CGColor;
//add it to our view
[layerView.layer addSublayer:colorLayer];
}
- (void)changeColorAnimationDelegateAction {
//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
[colorLayer addAnimation:animation forKey:nil];
}
/*
- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag {
//set backgroundColor property to match animation toValue
[CATransaction begin];
[CATransaction setDisableActions:YES];
colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;
[CATransaction commit];
}
*/
//clockViewAnimation
- (void)clockViewAnimationTest {
//adjust anchor points
hourHand = [[UIImageView alloc] init];
hourHand.frame = CGRectMake(100.0, 100.0f, 2.f, 60.0f);
hourHand.backgroundColor = [UIColor redColor];
hourHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
[self.view addSubview:hourHand];
minuteHand = [[UIImageView alloc] init];
minuteHand.frame = CGRectMake(100.0, 100.0f, 2.f, 60.0f);
minuteHand.backgroundColor = [UIColor blueColor];
minuteHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
[self.view addSubview:minuteHand];
secondHand = [[UIImageView alloc] init];
secondHand.frame = CGRectMake(100.0, 100.0f, 2.f, 60.0f);
secondHand.backgroundColor = [UIColor greenColor];
secondHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
[self.view addSubview:secondHand];
//start timer
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tickAnimation) userInfo:nil repeats:YES];
//set initial hand positions
[self updateHandsAnimated:NO];
}
- (void)tickAnimation {
[self updateHandsAnimated:YES];
}
- (void)updateHandsAnimated:(BOOL)animated {
//convert time to hours, minutes and seconds
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSUInteger units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];
CGFloat hourAngle = (components.hour / 12.0) * M_PI * 2.0;
//calculate hour hand angle //calculate minute hand angle
CGFloat minuteAngle = (components.minute / 60.0) * M_PI * 2.0;
//calculate second hand angle
CGFloat secondAngle = (components.second / 60.0) * M_PI * 2.0;
//rotate hands
[self setAngle:hourAngle forHand:hourHand animated:animated];
[self setAngle:minuteAngle forHand:minuteHand animated:animated];
[self setAngle:secondAngle forHand:secondHand animated:animated];
}
/*
- (void)setAngle:(CGFloat)angle forHand:(UIView *)handView animated:(BOOL)animated {
//generate transform
CATransform3D transform = CATransform3DMakeRotation(angle, 0, 0, 1);
if (animated) {
// create transform animation
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"];
[handView.layer addAnimation:animation forKey:nil];
} else {
//set transform directly
handView.layer.transform = transform;
}
}
*/
/*
- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag {
//set final position for hand view
UIView *handView = [anim valueForKey:@"handView"];
handView.layer.transform = [anim.toValue CATransform3DValue];
}
*/
关键帧动画
//CAKeyframeAnimation同样是CAPropertyAnimation的一个子类,它依然作用于单一的一个属性,但是和CABasicAnimation不一样的是,它不限制于设置一个起始和结束的值,而是可以根据一连串随意的值来做动画。
- (void)keyframeAnimationTest {
layerView = [[UIView alloc] init];
layerView.frame = CGRectMake(0, 20, 320, 320);
[self.view addSubview:layerView];
//add change color button
UIButton *changeColorBtn = [UIButton buttonWithType:UIButtonTypeCustom];
changeColorBtn.frame = CGRectMake(50, 200, 200, 44.0);
[changeColorBtn setTitle:@"change Color" forState:(UIControlStateNormal)];
[changeColorBtn addTarget:self action:@selector(keyAnimationChangeColor) forControlEvents:UIControlEventTouchUpInside];
changeColorBtn.backgroundColor = [UIColor redColor];
[layerView addSubview:changeColorBtn];
//create sublayer
colorLayer = [CALayer layer];
colorLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0f);
colorLayer.backgroundColor = [UIColor blueColor].CGColor;
//add it to our view
[layerView.layer addSublayer:colorLayer];
}
- (void)keyAnimationChangeColor {
//create a key frame animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"backgroundColor";
animation.duration = 2.0f;
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
[colorLayer addAnimation:animation forKey:nil];
}
//CGPath
- (void)keyframeAnimationPathTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(0.0, 0.0, 320, 320);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
//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;
[containerView.layer addSublayer:pathLayer];
//add the ship
CALayer *starLayer = [CALayer layer];
starLayer.frame = CGRectMake(0, 0, 64, 64);
starLayer.position = CGPointMake(0, 150);
starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
[containerView.layer addSublayer:starLayer];
//create the keyframe animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
animation.duration = 4.0;
animation.path = bezierPath.CGPath;
//指向曲线切线的方向
animation.rotationMode = kCAAnimationRotateAuto;
[starLayer addAnimation:animation forKey:nil];
}
虚拟属性
- (void)valueRotationTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(0.0, 0.0, 320, 320);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
//add the ship
CALayer *starLayer = [CALayer layer];
starLayer.frame = CGRectMake(0, 0, 128, 128);
starLayer.position = CGPointMake(150, 150);
starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
[containerView.layer addSublayer:starLayer];
//animate the star rotation
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform";
animation.duration = 2.0;
//从M_PI(180度)调整到2 * M_PI(360度),然后运行程序,会发现这时候飞船完全不动了
//用byValue而不是toValue, 没有做任何旋转,这是因为变换矩阵不能像角度值那样叠加
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 0, 1)];
[starLayer addAnimation:animation forKey:nil];
}
//transform.rotation属性有一个奇怪的问题是它其实并不存在。这是因为CATransform3D并不是一个对象,它实际上是一个结构体,也没有符合KVC相关属性,transform.rotation实际上是一个CALayer用于处理动画变换的虚拟属性。
//当你对他们做动画时,Core Animation自动地根据通过CAValueFunction来计算的值来更新transform属性。
//CAValueFunction用于把我们赋给虚拟的transform.rotation简单浮点值转换成真正的用于摆放图层的CATransform3D矩阵值。你可以通过设置CAPropertyAnimation的valueFunction属性来改变,于是你设置的函数将会覆盖默认的函数。
//用transform.rotation而不是transform做动画的好处如下:
//我们可以不通过关键帧一步旋转多于180度的动画。
//可以用相对值而不是绝对值旋转(设置byValue而不是toValue)。
//可以不用创建CATransform3D,而是使用一个简单的数值来指定角度。
//不会和transform.position或者transform.scale冲突(同样是使用关键路径来做独立的动画属性)。
- (void)virtualValueRotationTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(0.0, 0.0, 320, 320);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
//add the ship
CALayer *starLayer = [CALayer layer];
starLayer.frame = CGRectMake(0, 0, 128, 128);
starLayer.position = CGPointMake(150, 150);
starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
[containerView.layer addSublayer:starLayer];
//animate the star rotation
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform.rotation";
animation.duration = 2.0;
animation.toValue = @(M_PI *2);
[starLayer addAnimation:animation forKey:nil];
}
动画组
//CAAnimationGroup是另一个继承于CAAnimation的子类,它添加了一个animations数组的属性,用来组合别的动画。
- (void)animationGroupTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(0.0, 0.0, 320, 320);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
//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;
[containerView.layer addSublayer:pathLayer];
//add a colored layer
colorLayer = [CALayer layer];
colorLayer.frame = CGRectMake(0, 0, 64, 64);
colorLayer.backgroundColor = [UIColor greenColor].CGColor;
[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 the animation group
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = @[animation1, animation2];
groupAnimation.duration = 4.0;
//add the animation to the color layer
[colorLayer addAnimation:groupAnimation forKey:nil];
}
过渡
//CATransition:CAAnimation的子类,有一个type和subtype来标识变换效果。
//type属性是一个NSString类型
//kCATransitionFade:平滑的淡入淡出效果
//kCATransitionMoveIn:从顶部滑动进入
//kCATransitionPush:它创建了一个新的图层,从边缘的一侧滑动进来,把旧图层从另一侧推出去的效果。
//kCATransitionReveal:把原始的图层滑动出去来显示新的外观,而不是把新的图层滑动进入
//subtype
//kCATransitionFromRight
//kCATransitionFromLeft
//kCATransitionFromTop
//kCATransitionFromBottom
- (void)transitionTest {
imageView = [[UIImageView alloc] init];
imageView.frame = CGRectMake(0, 20, 320, 320);
imageView.backgroundColor = [UIColor blackColor];
[self.view addSubview:imageView];
UIButton *btnSwitchImage = [UIButton buttonWithType:UIButtonTypeCustom];
btnSwitchImage.frame = CGRectMake(20, 350, 200, 44);
btnSwitchImage.backgroundColor = [UIColor blueColor];
[btnSwitchImage setTitle:@"Switch Image" forState:UIControlStateNormal];
[btnSwitchImage addTarget:self action:@selector(switchImageAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btnSwitchImage];
//set up images
images = @[
[UIImage imageNamed:@"Meal"],
[UIImage imageNamed:@"Star"],
[UIImage imageNamed:@"Meal"],
[UIImage imageNamed:@"Star"]
];
}
- (void)switchImageAction {
//set up crossfade transition
CATransition *transition = [CATransition animation];
transition.type = kCATransitionReveal;
//apply transition to imageView backing layer
[imageView.layer addAnimation:transition forKey:nil];
//cycle to next image
UIImage *currentImage = imageView.image;
NSUInteger index = [images indexOfObject:currentImage];
index = (index + 1) % [images count];
imageView.image = images[index];
}
隐式过渡
//当设置了CALayer的content属性的时候,CATransition的确是默认的行为
//对于视图关联的图层,或者是其他隐式动画的行为,这个特性依然是被禁用的,但是对于你自己创建的图层,这意味着对图层contents图片做的改动都会自动附上淡入淡出的动画。
//对图层树的动画
//to appdelegate first and second viewController
//自定义动画
//更奇怪的是苹果通过UIView +transitionFromView:toView:duration:options:completion:和+transitionWithView:duration:options:animations:方法提供了Core Animation的过渡特性。但是这里的可用的过渡选项和CATransition的type属性提供的常量完全不同。
//UIView过渡方法中options参数可以由如下常量指定:
//UIViewAnimationOptionTransitionFlipFromLeft
//UIViewAnimationOptionTransitionFlipFromRight
//UIViewAnimationOptionTransitionCurlUp
//UIViewAnimationOptionTransitionCurlDown
//UIViewAnimationOptionTransitionCrossDissolve
//UIViewAnimationOptionTransitionFlipFromTop
//UIViewAnimationOptionTransitionFlipFromBottom
- (void)transitionSelfTest {
imageView = [[UIImageView alloc] init];
imageView.frame = CGRectMake(0, 20, 320, 320);
imageView.backgroundColor = [UIColor blackColor];
[self.view addSubview:imageView];
UIButton *btnSwitchImage = [UIButton buttonWithType:UIButtonTypeCustom];
btnSwitchImage.frame = CGRectMake(20, 350, 200, 44);
btnSwitchImage.backgroundColor = [UIColor blueColor];
[btnSwitchImage setTitle:@"Switch Image" forState:UIControlStateNormal];
[btnSwitchImage addTarget:self action:@selector(selfSwitchImageAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btnSwitchImage];
//set up images
images = @[
[UIImage imageNamed:@"Meal"],
[UIImage imageNamed:@"Star"],
[UIImage imageNamed:@"Meal"],
[UIImage imageNamed:@"Star"]
];
}
- (void)selfSwitchImageAction {
[UIView transitionWithView:imageView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
//cycle to next image
UIImage *currentImage = imageView.image;
NSUInteger index = [images indexOfObject:currentImage];
index = (index + 1) % [images count];
imageView.image = images[index];
}
completion:nil];
}
//对图层做截图还是很简单的。CALayer有一个-renderInContext:方法,可以通过把它绘制到Core Graphics的上下文中捕获当前内容的图片,然后在另外的视图中显示出来。
//为了让事情更简单,我们用UIView -animateWithDuration:completion:方法来实现。虽然用CABasicAnimation可以达到同样的效果,但是那样的话我们就需要对图层的变换和不透明属性创建单独的动画,然后当动画结束的是哦户在CAAnimationDelegate中把coverView从屏幕中移除。
//用renderInContext:创建自定义过渡效果
- (void)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];
}];
}
在动画过程中取消动画
//你可以用-addAnimation:forKey:方法中的key参数来在添加动画之后检索一个动画,使用如下方法:
//- (CAAnimation *)animationForKey:(NSString *)key;
//- (void)removeAnimationForKey:(NSString *)key;
//- (void)removeAllAnimations;
//动画在结束之后被自动移除,除非设置removedOnCompletion为NO,如果你设置动画在结束之后不被自动移除,那么当它不需要的时候你要手动移除它;否则它会一直存在于内存中,直到图层被销毁。
- (void)cancelAnimationTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
UIButton *startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
startBtn.frame = CGRectMake(0.0, 400, 100, 44.0f);
[startBtn setTitle:@"Star" forState:UIControlStateNormal];
[startBtn addTarget:self action:@selector(startAction) forControlEvents:UIControlEventTouchUpInside];
[startBtn setBackgroundColor:[UIColor blueColor]];
[self.view addSubview:startBtn];
UIButton *stopBtn = [UIButton buttonWithType:UIButtonTypeCustom];
stopBtn.frame = CGRectMake(120.0, 400, 100, 44.0f);
[stopBtn setTitle:@"Stop" forState:UIControlStateNormal];
[stopBtn addTarget:self action:@selector(stopAction) forControlEvents:UIControlEventTouchUpInside];
[stopBtn setBackgroundColor:[UIColor blueColor]];
[self.view addSubview:stopBtn];
//add the star
starLayer = [CALayer layer];
starLayer.frame = CGRectMake(0, 0, 128, 128);
starLayer.position = CGPointMake(150, 150);
starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
[containerView.layer addSublayer:starLayer];
}
- (void)startAction {
//animate the ship rotation
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform.rotation";
animation.duration = 2.0;
animation.byValue = @(M_PI * 2);
animation.delegate = self;
[starLayer addAnimation:animation forKey:@"rotateAnimation"];
}
- (void)stopAction {
[starLayer removeAnimationForKey:@"rotateAnimation"];
}
/*
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
//log that the animation stopped
NSLog(@"The animation stopped (finished:%@", flag? @"Yes":@"No");
}
*/
pragma mark -- 图层时间
//CAMediaTiming协议
//CAMediaTiming协议定义了在一段动画内用来控制逝去时间的属性的集合,CALayer和CAAnimation都实现了这个协议,所以时间可以被任意基于一个图层或者一段动画的类控制。
//持续和重复
//duration是一个CFTimeInterval的类型(类似于NSTimeInterval的一种双精度浮点类型),对将要进行的动画的一次迭代指定了时间。
//CAMediaTiming另外还有一个属性叫做repeatCount,代表动画重复的迭代次数。如果duration是2,repeatCount设为3.5(三个半迭代),那么完整的动画时长将是7秒。
//duration和repeatCount默认都是0,这里的0仅仅代表了“默认”,也就是0.25秒和1次
- (void)durationAndRepeatCountTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
startBtn.frame = CGRectMake(0.0, 400, 100, 44.0f);
[startBtn setTitle:@"Star" forState:UIControlStateNormal];
[startBtn addTarget:self action:@selector(durationStartAction) forControlEvents:UIControlEventTouchUpInside];
[startBtn setBackgroundColor:[UIColor blueColor]];
[self.view addSubview:startBtn];
//add the star
starLayer = [CALayer layer];
starLayer.frame = CGRectMake(0, 0, 128, 128);
starLayer.position = CGPointMake(150, 150);
starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
[containerView.layer addSublayer:starLayer];
}
- (void)setControlsEnabled:(BOOL)enbled {
startBtn.enabled = enbled;
startBtn.alpha = enbled? 1.0f:0.25;
}
- (void)durationStartAction {
CFTimeInterval duration = 3.0f;
float repeatCount = 2.5;
//animate the star rotation
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform.rotation";
animation.duration = duration;
animation.repeatCount = repeatCount;
animation.byValue = @(M_PI * 2);
animation.delegate = self;
[starLayer addAnimation:animation forKey:@"rotateAnimation"];
//disable controls
[self setControlsEnabled:NO];
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
[self setControlsEnabled:YES];
}
//repeatDuration属性:它让动画重复一个指定的时间,而不是指定次数。
//autoreverses的属性:(BOOL类型)在每次间隔交替循环过程中自动回放
//把repeatDuration设置为INFINITY,于是动画无限循环播放,设置repeatCount为INFINITY也有同样的效果
- (void)repeatDurationTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
//add the door
CALayer *doorLayer = [CALayer layer];
doorLayer.frame = CGRectMake(0, 0, 128, 256);
doorLayer.position = CGPointMake(150-64, 150);
doorLayer.anchorPoint = CGPointMake(0, 0.5);
doorLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
[containerView.layer addSublayer:doorLayer];
//apply perspective transform
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0 / 500.0;
containerView.layer.sublayerTransform = perspective;
//apply swinging animation
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform.rotation.y";
animation.toValue = @(-M_PI_2);
animation.duration = 2.0;
animation.repeatDuration = INFINITY;
animation.autoreverses = YES;
[doorLayer addAnimation:animation forKey:nil];
}
相对时间
//beginTime指定了动画开始之前的的延迟时间
//speed是一个时间的倍数,默认1.0,减少它会减慢图层/动画的时间,增加它会加快速度。
//timeOffset:增加timeOffset只是让动画快进到某一点;对于一个持续1秒的动画来说,设置timeOffset为0.5意味着动画将从一半的地方开始
//timeOffset并不受speed的影响,。所以如果你把speed设为2.0,把timeOffset设置为0.5,那么你的动画将从动画最后结束的地方开始,因为1秒的动画实际上被缩短到了0.5秒。然而即使使用了timeOffset让动画从结束的地方开始,它仍然播放了一个完整的时长,这个动画仅仅是循环了一圈,然后从头开始播放。
- (void)timeOffsetAndSpeedTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
speedLabel = [[UILabel alloc] init];
speedLabel.frame = CGRectMake(0.0, 400.0, 100, 40.0f);
speedLabel.backgroundColor = [UIColor grayColor];
[self.view addSubview:speedLabel];
speedSlider = [[UISlider alloc] init];
speedSlider.frame = CGRectMake(0.0, 450, 320, 40);
speedSlider.backgroundColor = [UIColor purpleColor];
[speedSlider addTarget:self action:@selector(updateSliders) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:speedSlider];
timeOffsetLabel = [[UILabel alloc] init];
timeOffsetLabel.frame = CGRectMake(120, 400.0, 100, 40.0f);
timeOffsetLabel.backgroundColor = [UIColor grayColor];
[self.view addSubview:timeOffsetLabel];
timeOffsetSlider = [[UISlider alloc] init];
timeOffsetSlider.frame = CGRectMake(0.0, 500, 320, 40);
timeOffsetSlider.backgroundColor = [UIColor purpleColor];
[timeOffsetSlider addTarget:self action:@selector(updateSliders) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:timeOffsetSlider];
startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
startBtn.frame = CGRectMake(0.0, 550, 100, 44.0f);
[startBtn setTitle:@"Star" forState:UIControlStateNormal];
[startBtn addTarget:self action:@selector(playAction) forControlEvents:UIControlEventTouchUpInside];
[startBtn setBackgroundColor:[UIColor blueColor]];
[self.view addSubview:startBtn];
//create a path
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;
[containerView.layer addSublayer:pathLayer];
//add the star
starLayer = [CALayer layer];
starLayer.frame = CGRectMake(0, 0, 64, 64);
starLayer.position = CGPointMake(0, 150);
starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
[containerView.layer addSublayer:starLayer];
//set initial values
[self updateSliders];
}
- (void)updateSliders {
CFTimeInterval timeOffset = timeOffsetSlider.value;
timeOffsetLabel.text = [NSString stringWithFormat:@"%f", timeOffset];
float speed = speedSlider.value;
speedLabel.text = [NSString stringWithFormat:@"%f", speed];
}
- (void)playAction {
//create the keyframe animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
animation.timeOffset = timeOffsetSlider.value;
animation.speed = speedSlider.value;
animation.duration = 1.0;
animation.path = bezierPath.CGPath;
animation.rotationMode = kCAAnimationRotateAuto;
animation.removedOnCompletion = NO;
[starLayer addAnimation:animation forKey:@"slide"];
}
fillMode
//一种可能是属性和动画没被添加之前保持一致,也就是在模型图层定义的值(见第七章“隐式动画”,模型图层和呈现图层的解释)。
//另一种可能是保持动画开始之前那一帧,或者动画结束之后的那一帧。这就是所谓的填充,因为动画开始和结束的值用来填充开始之前和结束之后的时间。
//需要把removeOnCompletion设置为NO,另外需要给动画添加一个非空的键,于是可以在不需要动画的时候把它从图层上移除。
//fillMode:NSString类型
//kCAFillModeForwards
//kCAFillModeBackwards
//kCAFillModeBoth
//kCAFillModeRemoved 默认
层级关系时间
//对图层调整时间将会影响到它本身和子图层的动画,但不会影响到父图层。另一个相似点是所有的动画都被按照层级组合(使用CAAnimationGroup实例)
//对CALayer或者CAGroupAnimation调整duration和repeatCount/repeatDuration属性并不会影响到子动画。但是beginTime,timeOffset和speed属性将会影响到子动画。
全局时间和本地时间
//全局时间
//CACurrentMediaTime函数来访问马赫时间:
//CFTimeInterval time = CACurrentMediaTime();//它返回了设备自从上次启动后的秒数,并不是你所关心的,它真实的作用在于对动画的时间测量提供了一个相对值。注意当设备休眠的时候马赫时间会暂停,也就是所有的CAAnimations(基于马赫时间)同样也会暂停。
//每个CALayer和CAAnimation实例都有自己本地时间的概念,是根据父图层/动画层级关系中的beginTime,timeOffset和speed属性计算。就和转换不同图层之间坐标关系一样,CALayer同样也提供了方法来转换不同图层之间的本地时间。如下:
//- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
//- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
//当用来同步不同图层之间有不同的speed,timeOffset和beginTime的动画,这些方法会很有用。
暂停,倒回和快进
//可以利用CAMediaTiming来暂停图层本身
//如果把图层的speed设置成0,它会暂停任何添加到图层上的动画。类似的,设置speed大于1.0将会快进,设置成一个负值将会倒回动画。
//self.window.layer.speed = 100;
手动动画
//timeOffset一个很有用的功能在于你可以它可以让你手动控制动画进程,通过设置speed为0,可以禁用动画的自动播放,然后来使用timeOffset来来回显示动画序列。这可以使得运用手势来手动控制动画变得很简单。
- (void)handleAnimationTest {
containerView = [[UIView alloc] init];
containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
containerView.backgroundColor = [UIColor blackColor];
[self.view addSubview:containerView];
//add the door
starLayer = [CALayer layer];
starLayer.frame = CGRectMake(0, 0, 128, 256);
starLayer.position = CGPointMake(150-64, 150);
starLayer.anchorPoint = CGPointMake(0, 0.5);
starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
[containerView.layer addSublayer:starLayer];
//apply perspective transform
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0 / 500.0;
containerView.layer.sublayerTransform = perspective;
//add pan gesture recognizer to handle swipes
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] init];
[pan addTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];
//pause all layer animations
starLayer.speed = 0.0;
//apply swinging animation (which won't play because layer is paused)
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"transform.rotation.y";
animation.toValue = @(-M_PI_2);
animation.duration = 2.0;
animation.repeatDuration = INFINITY;
animation.autoreverses = YES;
[starLayer addAnimation:animation forKey:nil];
}
- (void)pan:(UIPanGestureRecognizer *)pan {
//get horizontal component of pan gesture
CGFloat x = [pan translationInView:self.view].x;
//conver from points to animation duration //using a reasonable scale factor
x /= 200.0f;
//update timeOffset and clamp result
CFTimeInterval timeOffset = starLayer.timeOffset;
timeOffset = MIN(0.999, MAX(0.0, timeOffset - x));
starLayer.timeOffset = timeOffset;
//reset pan gesture
[pan setTranslation:CGPointZero inView:self.view];
}