//CABasicAnimation
//[self basicAnimationTest];
//animationDelegate
//[self animationDelegateTest];
//clockViewAnimationTest
//[self clockViewAnimationTest];
//keyframeAnimation
//[self keyframeAnimationTest];
//CGPath
//[self keyframeAnimationPathTest];
//虛擬屬性
//[self valueRotationTest];
//[self virtualValueRotationTest];
//動畫數(shù)組
//[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];
屬性動畫
//屬性動畫:作用于圖層的某個單一屬性,并指定了它的一個目標(biāo)值筐带,或者一連串將要做動畫的值。 分為兩種:基礎(chǔ)和關(guān)鍵幀
基礎(chǔ)動畫
//CABasicAnimation是CAPropertyAnimation的一個子類,而CAPropertyAnimation的父類是CAAnimation窿冯,CAAnimation同時也是Core Animation所有動畫類型的抽象基類。
//CAAnimation提供了一個計時函數(shù),一個委托(用于反饋動畫狀態(tài))以及一個removedOnCompletion确徙,用于標(biāo)識動畫是否該在結(jié)束后自動釋放(默認(rèn)YES醒串,為了防止內(nèi)存泄露)。CAAnimation同時實現(xiàn)了一些協(xié)議鄙皇,包括CAAction(允許CAAnimation的子類可以提供圖層行為)芜赌,以及CAMediaTiming
//CAPropertyAnimation : keyPath 僅可以作用于圖層本身的屬性,而且還包含了它的子成員的屬性伴逸,甚至是一些虛擬的屬性
//CABasicAnimation繼承于CAPropertyAnimation缠沈,add property id fromValue id toValue id byValue
//fromValue代表了動畫開始之前屬性的值,toValue代表了動畫結(jié)束之后的值错蝴,byValue代表了動畫執(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";
//因為動畫并沒有改變圖層的模型,而只是呈現(xiàn), 一旦動畫結(jié)束并從圖層上移除之后顷锰,圖層就立刻恢復(fù)到之前定義的外觀狀態(tài) 從沒改變過backgroundColor屬性柬赐,所以圖層就返回到原始的顏色
//在使用隱式動畫的時候,實際上它就是用例子中CABasicAnimation來實現(xiàn)的(回憶第七章官紫,我們在-actionForLayer:forKey:委托方法打印出來的結(jié)果就是CABasicAnimation)肛宋。
//animation.fromValue = (__bridge id)colorLayer.backgroundColor;
//colorLayer.backgroundColor = color.CGColor;
//由于這里的圖層并不是UIView關(guān)聯(lián)的圖層,我們需要用CATransaction來禁用隱式動畫行為束世,否則默認(rèn)的圖層行為會干擾我們的顯式動畫
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];
}
//避免在每次動畫時候都重復(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)鍵幀動畫
//CAKeyframeAnimation同樣是CAPropertyAnimation的一個子類,它依然作用于單一的一個屬性良狈,但是和CABasicAnimation不一樣的是后添,它不限制于設(shè)置一個起始和結(jié)束的值,而是可以根據(jù)一連串隨意的值來做動畫。
- (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 = @[
//為了動畫的平滑特性遇西,我們需要開始和結(jié)束的關(guān)鍵幀來匹配當(dāng)前屬性的值
(__bridge id)[UIColor blueColor].CGColor,//不能自動把當(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度)馅精,然后運行程序,會發(fā)現(xiàn)這時候飛船完全不動了
//用byValue而不是toValue, 沒有做任何旋轉(zhuǎn)粱檀,這是因為變換矩陣不能像角度值那樣疊加
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 0, 1)];
[starLayer addAnimation:animation forKey:nil];
}
//transform.rotation屬性有一個奇怪的問題是它其實并不存在洲敢。這是因為CATransform3D并不是一個對象,它實際上是一個結(jié)構(gòu)體茄蚯,也沒有符合KVC相關(guān)屬性压彭,transform.rotation實際上是一個CALayer用于處理動畫變換的虛擬屬性。
//當(dāng)你對他們做動畫時渗常,Core Animation自動地根據(jù)通過CAValueFunction來計算的值來更新transform屬性壮不。
//CAValueFunction用于把我們賦給虛擬的transform.rotation簡單浮點值轉(zhuǎn)換成真正的用于擺放圖層的CATransform3D矩陣值。你可以通過設(shè)置CAPropertyAnimation的valueFunction屬性來改變皱碘,于是你設(shè)置的函數(shù)將會覆蓋默認(rèn)的函數(shù)询一。
//用transform.rotation而不是transform做動畫的好處如下:
//我們可以不通過關(guān)鍵幀一步旋轉(zhuǎn)多于180度的動畫。
//可以用相對值而不是絕對值旋轉(zhuǎn)(設(shè)置byValue而不是toValue)癌椿。
//可以不用創(chuàng)建CATransform3D健蕊,而是使用一個簡單的數(shù)值來指定角度。
//不會和transform.position或者transform.scale沖突(同樣是使用關(guān)鍵路徑來做獨立的動畫屬性)踢俄。
- (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數(shù)組的屬性,用來組合別的動畫都办。
- (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來標(biāo)識變換效果嫡锌。
//type屬性是一個NSString類型
//kCATransitionFade:平滑的淡入淡出效果
//kCATransitionMoveIn:從頂部滑動進入
//kCATransitionPush:它創(chuàng)建了一個新的圖層,從邊緣的一側(cè)滑動進來脆丁,把舊圖層從另一側(cè)推出去的效果世舰。
//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];
}
隱式過渡
//當(dāng)設(shè)置了CALayer的content屬性的時候槽卫,CATransition的確是默認(rèn)的行為
//對于視圖關(guān)聯(lián)的圖層,或者是其他隱式動畫的行為胰蝠,這個特性依然是被禁用的歼培,但是對于你自己創(chuàng)建的圖層,這意味著對圖層contents圖片做的改動都會自動附上淡入淡出的動畫茸塞。
//對圖層樹的動畫
//to appdelegate first and second viewController
//自定義動畫
//更奇怪的是蘋果通過UIView +transitionFromView:toView:duration:options:completion:和+transitionWithView:duration:options:animations:方法提供了Core Animation的過渡特性躲庄。但是這里的可用的過渡選項和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有一個-renderInContext:方法噪窘,可以通過把它繪制到Core Graphics的上下文中捕獲當(dāng)前內(nèi)容的圖片,然后在另外的視圖中顯示出來效扫。
//為了讓事情更簡單倔监,我們用UIView -animateWithDuration:completion:方法來實現(xiàn)直砂。雖然用CABasicAnimation可以達到同樣的效果,但是那樣的話我們就需要對圖層的變換和不透明屬性創(chuà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];
}];
}
在動畫過程中取消動畫
//你可以用-addAnimation:forKey:方法中的key參數(shù)來在添加動畫之后檢索一個動畫,使用如下方法:
//- (CAAnimation *)animationForKey:(NSString *)key;
//- (void)removeAnimationForKey:(NSString *)key;
//- (void)removeAllAnimations;
//動畫在結(jié)束之后被自動移除谱秽,除非設(shè)置removedOnCompletion為NO洽蛀,如果你設(shè)置動畫在結(jié)束之后不被自動移除,那么當(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 -- 圖層時間
//CAMediaTiming協(xié)議
//CAMediaTiming協(xié)議定義了在一段動畫內(nèi)用來控制逝去時間的屬性的集合听绳,CALayer和CAAnimation都實現(xiàn)了這個協(xié)議颂碘,所以時間可以被任意基于一個圖層或者一段動畫的類控制。
//持續(xù)和重復(fù)
//duration是一個CFTimeInterval的類型(類似于NSTimeInterval的一種雙精度浮點類型)椅挣,對將要進行的動畫的一次迭代指定了時間头岔。
//CAMediaTiming另外還有一個屬性叫做repeatCount,代表動畫重復(fù)的迭代次數(shù)鼠证。如果duration是2峡竣,repeatCount設(shè)為3.5(三個半迭代),那么完整的動畫時長將是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屬性:它讓動畫重復(fù)一個指定的時間,而不是指定次數(shù)荠列。
//autoreverses的屬性:(BOOL類型)在每次間隔交替循環(huán)過程中自動回放
//把repeatDuration設(shè)置為INFINITY类浪,于是動畫無限循環(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];
}
相對時間
//beginTime指定了動畫開始之前的的延遲時間
//speed是一個時間的倍數(shù)肌似,默認(rèn)1.0费就,減少它會減慢圖層/動畫的時間,增加它會加快速度川队。
//timeOffset:增加timeOffset只是讓動畫快進到某一點;對于一個持續(xù)1秒的動畫來說力细,設(shè)置timeOffset為0.5意味著動畫將從一半的地方開始
//timeOffset并不受speed的影響,。所以如果你把speed設(shè)為2.0固额,把timeOffset設(shè)置為0.5眠蚂,那么你的動畫將從動畫最后結(jié)束的地方開始,因為1秒的動畫實際上被縮短到了0.5秒斗躏。然而即使使用了timeOffset讓動畫從結(jié)束的地方開始逝慧,它仍然播放了一個完整的時長,這個動畫僅僅是循環(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
//一種可能是屬性和動畫沒被添加之前保持一致栅干,也就是在模型圖層定義的值(見第七章“隱式動畫”,模型圖層和呈現(xiàn)圖層的解釋)捐祠。
//另一種可能是保持動畫開始之前那一幀碱鳞,或者動畫結(jié)束之后的那一幀。這就是所謂的填充踱蛀,因為動畫開始和結(jié)束的值用來填充開始之前和結(jié)束之后的時間窿给。
//需要把removeOnCompletion設(shè)置為NO,另外需要給動畫添加一個非空的鍵率拒,于是可以在不需要動畫的時候把它從圖層上移除崩泡。
//fillMode:NSString類型
//kCAFillModeForwards
//kCAFillModeBackwards
//kCAFillModeBoth
//kCAFillModeRemoved 默認(rèn)
層級關(guān)系時間
//對圖層調(diào)整時間將會影響到它本身和子圖層的動畫,但不會影響到父圖層猬膨。另一個相似點是所有的動畫都被按照層級組合(使用CAAnimationGroup實例)
//對CALayer或者CAGroupAnimation調(diào)整duration和repeatCount/repeatDuration屬性并不會影響到子動畫角撞。但是beginTime,timeOffset和speed屬性將會影響到子動畫勃痴。
全局時間和本地時間
//全局時間
//CACurrentMediaTime函數(shù)來訪問馬赫時間:
//CFTimeInterval time = CACurrentMediaTime();//它返回了設(shè)備自從上次啟動后的秒數(shù)谒所,并不是你所關(guān)心的,它真實的作用在于對動畫的時間測量提供了一個相對值。注意當(dāng)設(shè)備休眠的時候馬赫時間會暫停沛申,也就是所有的CAAnimations(基于馬赫時間)同樣也會暫停劣领。
//每個CALayer和CAAnimation實例都有自己本地時間的概念,是根據(jù)父圖層/動畫層級關(guān)系中的beginTime铁材,timeOffset和speed屬性計算尖淘。就和轉(zhuǎn)換不同圖層之間坐標(biāo)關(guān)系一樣,CALayer同樣也提供了方法來轉(zhuǎn)換不同圖層之間的本地時間著觉。如下:
//- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
//- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
//當(dāng)用來同步不同圖層之間有不同的speed村生,timeOffset和beginTime的動畫,這些方法會很有用饼丘。
暫停趁桃,倒回和快進
//可以利用CAMediaTiming來暫停圖層本身
//如果把圖層的speed設(shè)置成0,它會暫停任何添加到圖層上的動畫葬毫。類似的镇辉,設(shè)置speed大于1.0將會快進屡穗,設(shè)置成一個負(fù)值將會倒回動畫贴捡。
//self.window.layer.speed = 100;
手動動畫
//timeOffset一個很有用的功能在于你可以它可以讓你手動控制動畫進程,通過設(shè)置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];
}