//CABasicAnimation
//[self basicAnimationTest];
//animationDelegate
//[self animationDelegateTest];
//clockViewAnimationTest
//[self clockViewAnimationTest];
//keyframeAnimation
//[self keyframeAnimationTest];
//CGPath
//[self keyframeAnimationPathTest];
//虛擬屬性
//[self valueRotationTest];
//[self virtualValueRotationTest];
//動(dòng)畫數(shù)組
//[self animationGroupTest];
//過渡
//[self transitionTest];
//自定義動(dòng)畫
//[self transitionSelfTest];
//截圖
//[self performTransition];
//取消動(dòng)畫
//[self cancelAnimationTest];
//duration repeatCount
//[self durationAndRepeatCountTest];
//repeatDuration
//[self repeatDurationTest];
//timeOffset and speed
//[self timeOffsetAndSpeedTest];
//handle animation
//[self handleAnimationTest];
屬性動(dòng)畫
//屬性動(dòng)畫:作用于圖層的某個(gè)單一屬性,并指定了它的一個(gè)目標(biāo)值,或者一連串將要做動(dòng)畫的值。 分為兩種:基礎(chǔ)和關(guān)鍵幀
基礎(chǔ)動(dòng)畫
//CABasicAnimation是CAPropertyAnimation的一個(gè)子類,而CAPropertyAnimation的父類是CAAnimation,CAAnimation同時(shí)也是Core Animation所有動(dòng)畫類型的抽象基類。
//CAAnimation提供了一個(gè)計(jì)時(shí)函數(shù),一個(gè)委托(用于反饋動(dòng)畫狀態(tài))以及一個(gè)removedOnCompletion,用于標(biāo)識動(dòng)畫是否該在結(jié)束后自動(dòng)釋放(默認(rèn)YES,為了防止內(nèi)存泄露)。CAAnimation同時(shí)實(shí)現(xiàn)了一些協(xié)議,包括CAAction(允許CAAnimation的子類可以提供圖層行為),以及CAMediaTiming
//CAPropertyAnimation : keyPath 僅可以作用于圖層本身的屬性,而且還包含了它的子成員的屬性,甚至是一些虛擬的屬性
//CABasicAnimation繼承于CAPropertyAnimation,add property id fromValue id toValue id byValue
//fromValue代表了動(dòng)畫開始之前屬性的值,toValue代表了動(dòng)畫結(jié)束之后的值,byValue代表了動(dòng)畫執(zhí)行過程中改變的值。 只需要指定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";
//因?yàn)閯?dòng)畫并沒有改變圖層的模型,而只是呈現(xiàn), 一旦動(dòng)畫結(jié)束并從圖層上移除之后,圖層就立刻恢復(fù)到之前定義的外觀狀態(tài) 從沒改變過backgroundColor屬性,所以圖層就返回到原始的顏色
//在使用隱式動(dòng)畫的時(shí)候,實(shí)際上它就是用例子中CABasicAnimation來實(shí)現(xiàn)的(回憶第七章,我們在-actionForLayer:forKey:委托方法打印出來的結(jié)果就是CABasicAnimation)。
//animation.fromValue = (__bridge id)colorLayer.backgroundColor;
//colorLayer.backgroundColor = color.CGColor;
//由于這里的圖層并不是UIView關(guān)聯(lián)的圖層,我們需要用CATransaction來禁用隱式動(dòng)畫行為,否則默認(rèn)的圖層行為會干擾我們的顯式動(dòng)畫
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];
}
//避免在每次動(dòng)畫時(shí)候都重復(fù)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];
}
*/
關(guān)鍵幀動(dòng)畫
//CAKeyframeAnimation同樣是CAPropertyAnimation的一個(gè)子類,它依然作用于單一的一個(gè)屬性,但是和CABasicAnimation不一樣的是,它不限制于設(shè)置一個(gè)起始和結(jié)束的值,而是可以根據(jù)一連串隨意的值來做動(dòng)畫。
- (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 = @[
//為了動(dòng)畫的平滑特性,我們需要開始和結(jié)束的關(guān)鍵幀來匹配當(dāng)前屬性的值
(__bridge id)[UIColor blueColor].CGColor,//不能自動(dòng)把當(dāng)前值作為第一幀
(__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度)調(diào)整到2 * M_PI(360度),然后運(yùn)行程序,會發(fā)現(xiàn)這時(shí)候飛船完全不動(dòng)了
//用byValue而不是toValue, 沒有做任何旋轉(zhuǎn),這是因?yàn)樽儞Q矩陣不能像角度值那樣疊加
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 0, 1)];
[starLayer addAnimation:animation forKey:nil];
}
//transform.rotation屬性有一個(gè)奇怪的問題是它其實(shí)并不存在。這是因?yàn)镃ATransform3D并不是一個(gè)對象,它實(shí)際上是一個(gè)結(jié)構(gòu)體,也沒有符合KVC相關(guān)屬性,transform.rotation實(shí)際上是一個(gè)CALayer用于處理動(dòng)畫變換的虛擬屬性。
//當(dāng)你對他們做動(dòng)畫時(shí),Core Animation自動(dòng)地根據(jù)通過CAValueFunction來計(jì)算的值來更新transform屬性。
//CAValueFunction用于把我們賦給虛擬的transform.rotation簡單浮點(diǎn)值轉(zhuǎn)換成真正的用于擺放圖層的CATransform3D矩陣值。你可以通過設(shè)置CAPropertyAnimation的valueFunction屬性來改變,于是你設(shè)置的函數(shù)將會覆蓋默認(rèn)的函數(shù)。
//用transform.rotation而不是transform做動(dòng)畫的好處如下:
//我們可以不通過關(guān)鍵幀一步旋轉(zhuǎn)多于180度的動(dòng)畫。
//可以用相對值而不是絕對值旋轉(zhuǎn)(設(shè)置byValue而不是toValue)。
//可以不用創(chuàng)建CATransform3D,而是使用一個(gè)簡單的數(shù)值來指定角度。
//不會和transform.position或者transform.scale沖突(同樣是使用關(guān)鍵路徑來做獨(dú)立的動(dòng)畫屬性)。
- (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];
}
動(dòng)畫組
//CAAnimationGroup是另一個(gè)繼承于CAAnimation的子類,它添加了一個(gè)animations數(shù)組的屬性,用來組合別的動(dòng)畫。
- (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的子類,有一個(gè)type和subtype來標(biāo)識變換效果。
//type屬性是一個(gè)NSString類型
//kCATransitionFade:平滑的淡入淡出效果
//kCATransitionMoveIn:從頂部滑動(dòng)進(jìn)入
//kCATransitionPush:它創(chuàng)建了一個(gè)新的圖層,從邊緣的一側(cè)滑動(dòng)進(jìn)來,把舊圖層從另一側(cè)推出去的效果。
//kCATransitionReveal:把原始的圖層滑動(dòng)出去來顯示新的外觀,而不是把新的圖層滑動(dòng)進(jìn)入
//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];
}
隱式過渡
//當(dāng)設(shè)置了CALayer的content屬性的時(shí)候,CATransition的確是默認(rèn)的行為
//對于視圖關(guān)聯(lián)的圖層,或者是其他隱式動(dòng)畫的行為,這個(gè)特性依然是被禁用的,但是對于你自己創(chuàng)建的圖層,這意味著對圖層contents圖片做的改動(dòng)都會自動(dòng)附上淡入淡出的動(dòng)畫。
//對圖層樹的動(dòng)畫
//to appdelegate first and second viewController
//自定義動(dòng)畫
//更奇怪的是蘋果通過UIView +transitionFromView:toView:duration:options:completion:和+transitionWithView:duration:options:animations:方法提供了Core Animation的過渡特性。但是這里的可用的過渡選項(xiàng)和CATransition的type屬性提供的常量完全不同。
//UIView過渡方法中options參數(shù)可以由如下常量指定:
//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有一個(gè)-renderInContext:方法,可以通過把它繪制到Core Graphics的上下文中捕獲當(dāng)前內(nèi)容的圖片,然后在另外的視圖中顯示出來。
//為了讓事情更簡單,我們用UIView -animateWithDuration:completion:方法來實(shí)現(xiàn)。雖然用CABasicAnimation可以達(dá)到同樣的效果,但是那樣的話我們就需要對圖層的變換和不透明屬性創(chuàng)建單獨(dú)的動(dòng)畫,然后當(dāng)動(dòng)畫結(jié)束的是哦戶在CAAnimationDelegate中把coverView從屏幕中移除。
//用renderInContext:創(chuàng)建自定義過渡效果
- (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];
}];
}
在動(dòng)畫過程中取消動(dòng)畫
//你可以用-addAnimation:forKey:方法中的key參數(shù)來在添加動(dòng)畫之后檢索一個(gè)動(dòng)畫,使用如下方法:
//- (CAAnimation *)animationForKey:(NSString *)key;
//- (void)removeAnimationForKey:(NSString *)key;
//- (void)removeAllAnimations;
//動(dòng)畫在結(jié)束之后被自動(dòng)移除,除非設(shè)置removedOnCompletion為NO,如果你設(shè)置動(dòng)畫在結(jié)束之后不被自動(dòng)移除,那么當(dāng)它不需要的時(shí)候你要手動(dòng)移除它;否則它會一直存在于內(nèi)存中,直到圖層被銷毀。
- (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 -- 圖層時(shí)間
//CAMediaTiming協(xié)議
//CAMediaTiming協(xié)議定義了在一段動(dòng)畫內(nèi)用來控制逝去時(shí)間的屬性的集合,CALayer和CAAnimation都實(shí)現(xiàn)了這個(gè)協(xié)議,所以時(shí)間可以被任意基于一個(gè)圖層或者一段動(dòng)畫的類控制。
//持續(xù)和重復(fù)
//duration是一個(gè)CFTimeInterval的類型(類似于NSTimeInterval的一種雙精度浮點(diǎn)類型),對將要進(jìn)行的動(dòng)畫的一次迭代指定了時(shí)間。
//CAMediaTiming另外還有一個(gè)屬性叫做repeatCount,代表動(dòng)畫重復(fù)的迭代次數(shù)。如果duration是2,repeatCount設(shè)為3.5(三個(gè)半迭代),那么完整的動(dòng)畫時(shí)長將是7秒。
//duration和repeatCount默認(rèn)都是0,這里的0僅僅代表了“默認(rèn)”,也就是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屬性:它讓動(dòng)畫重復(fù)一個(gè)指定的時(shí)間,而不是指定次數(shù)。
//autoreverses的屬性:(BOOL類型)在每次間隔交替循環(huán)過程中自動(dòng)回放
//把repeatDuration設(shè)置為INFINITY,于是動(dòng)畫無限循環(huán)播放,設(shè)置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];
}
相對時(shí)間
//beginTime指定了動(dòng)畫開始之前的的延遲時(shí)間
//speed是一個(gè)時(shí)間的倍數(shù),默認(rèn)1.0,減少它會減慢圖層/動(dòng)畫的時(shí)間,增加它會加快速度。
//timeOffset:增加timeOffset只是讓動(dòng)畫快進(jìn)到某一點(diǎn);對于一個(gè)持續(xù)1秒的動(dòng)畫來說,設(shè)置timeOffset為0.5意味著動(dòng)畫將從一半的地方開始
//timeOffset并不受speed的影響,。所以如果你把speed設(shè)為2.0,把timeOffset設(shè)置為0.5,那么你的動(dòng)畫將從動(dòng)畫最后結(jié)束的地方開始,因?yàn)?秒的動(dòng)畫實(shí)際上被縮短到了0.5秒。然而即使使用了timeOffset讓動(dòng)畫從結(jié)束的地方開始,它仍然播放了一個(gè)完整的時(shí)長,這個(gè)動(dòng)畫僅僅是循環(huán)了一圈,然后從頭開始播放。
- (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
//一種可能是屬性和動(dòng)畫沒被添加之前保持一致,也就是在模型圖層定義的值(見第七章“隱式動(dòng)畫”,模型圖層和呈現(xiàn)圖層的解釋)。
//另一種可能是保持動(dòng)畫開始之前那一幀,或者動(dòng)畫結(jié)束之后的那一幀。這就是所謂的填充,因?yàn)閯?dòng)畫開始和結(jié)束的值用來填充開始之前和結(jié)束之后的時(shí)間。
//需要把removeOnCompletion設(shè)置為NO,另外需要給動(dòng)畫添加一個(gè)非空的鍵,于是可以在不需要?jiǎng)赢嫷臅r(shí)候把它從圖層上移除。
//fillMode:NSString類型
//kCAFillModeForwards
//kCAFillModeBackwards
//kCAFillModeBoth
//kCAFillModeRemoved 默認(rèn)
層級關(guān)系時(shí)間
//對圖層調(diào)整時(shí)間將會影響到它本身和子圖層的動(dòng)畫,但不會影響到父圖層。另一個(gè)相似點(diǎn)是所有的動(dòng)畫都被按照層級組合(使用CAAnimationGroup實(shí)例)
//對CALayer或者CAGroupAnimation調(diào)整duration和repeatCount/repeatDuration屬性并不會影響到子動(dòng)畫。但是beginTime,timeOffset和speed屬性將會影響到子動(dòng)畫。
全局時(shí)間和本地時(shí)間
//全局時(shí)間
//CACurrentMediaTime函數(shù)來訪問馬赫時(shí)間:
//CFTimeInterval time = CACurrentMediaTime();//它返回了設(shè)備自從上次啟動(dòng)后的秒數(shù),并不是你所關(guān)心的,它真實(shí)的作用在于對動(dòng)畫的時(shí)間測量提供了一個(gè)相對值。注意當(dāng)設(shè)備休眠的時(shí)候馬赫時(shí)間會暫停,也就是所有的CAAnimations(基于馬赫時(shí)間)同樣也會暫停。
//每個(gè)CALayer和CAAnimation實(shí)例都有自己本地時(shí)間的概念,是根據(jù)父圖層/動(dòng)畫層級關(guān)系中的beginTime,timeOffset和speed屬性計(jì)算。就和轉(zhuǎn)換不同圖層之間坐標(biāo)關(guān)系一樣,CALayer同樣也提供了方法來轉(zhuǎn)換不同圖層之間的本地時(shí)間。如下:
//- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
//- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
//當(dāng)用來同步不同圖層之間有不同的speed,timeOffset和beginTime的動(dòng)畫,這些方法會很有用。
暫停,倒回和快進(jìn)
//可以利用CAMediaTiming來暫停圖層本身
//如果把圖層的speed設(shè)置成0,它會暫停任何添加到圖層上的動(dòng)畫。類似的,設(shè)置speed大于1.0將會快進(jìn),設(shè)置成一個(gè)負(fù)值將會倒回動(dòng)畫。
//self.window.layer.speed = 100;
手動(dòng)動(dòng)畫
//timeOffset一個(gè)很有用的功能在于你可以它可以讓你手動(dòng)控制動(dòng)畫進(jìn)程,通過設(shè)置speed為0,可以禁用動(dòng)畫的自動(dòng)播放,然后來使用timeOffset來來回顯示動(dòng)畫序列。這可以使得運(yùn)用手勢來手動(dòng)控制動(dòng)畫變得很簡單。
- (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];
}