我們修改layer屬性時(shí)默認(rèn)會(huì)有動(dòng)畫(huà)膘茎。動(dòng)畫(huà)使用CABasicAnimation對(duì)象粟耻,持續(xù)0.25。默認(rèn)會(huì)產(chǎn)生隱式動(dòng)畫(huà)的layer屬性:文檔連接
Core Animation使用action對(duì)象
來(lái)執(zhí)行我們修改屬性產(chǎn)生的隱式動(dòng)畫(huà)。那么什么是action對(duì)象
袄简?(action對(duì)象
是文檔的原文,翻譯叫隱式動(dòng)畫(huà)感覺(jué)有點(diǎn)奇怪泛啸,但是兩者應(yīng)該是等同的)
什么是action對(duì)象绿语?
action對(duì)象
遵照CAAction
協(xié)議,并且自定義了一些可能在layer上執(zhí)行的相關(guān)行為候址。比如CAAnimation類(lèi)吕粹,修改Layer屬性會(huì)產(chǎn)生動(dòng)畫(huà)就是通過(guò)執(zhí)行它生成的。
如何創(chuàng)建action對(duì)象岗仑?
action對(duì)象
遵照CAAction
協(xié)議匹耕,并執(zhí)行協(xié)議方法:[runActionForKey:object:arguments:]
。你可以在這個(gè)類(lèi)里進(jìn)行一些自定義設(shè)置荠雕。下面代碼創(chuàng)建了一個(gè)遵照CAAction
協(xié)議稳其,并且在協(xié)議方法里執(zhí)行CABasicAnimation
對(duì)象:
@interface CustomAction : NSObject<CAAction>
@property (nonatomic) CGColorRef currentColor;
@end
@implementation CustomAction
- (void)runActionForKey:(NSString *)key object:(id)anObject arguments:(NSDictionary *)dict {
CustomLayer *layer = anObject;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
animation.fromValue = (id)[UIColor greenColor].CGColor;
animation.toValue = (id)[UIColor redColor].CGColor;
animation.duration = 5;
[layer addAnimation:animation forKey:@"backgroundColor"];
}
@end
action對(duì)象觸發(fā)過(guò)程:
1.和action對(duì)象
有關(guān)的事件被觸發(fā)
2.創(chuàng)建對(duì)應(yīng)的action對(duì)象
3.執(zhí)行action對(duì)象
1.和action對(duì)象有關(guān)的事件被觸發(fā)
觸發(fā)事件包括:
- layer的屬性被修改。包括layer的任何屬性炸卑,不僅僅只是會(huì)產(chǎn)生動(dòng)畫(huà)的部分既鞠。
- layer被添加到layer階層。標(biāo)識(shí)符key是kCAOnOrder盖文。
- layer被移除layer階層嘱蛋。標(biāo)示符key是kCAOnOrderOut。
- layer將參與transition動(dòng)畫(huà)。標(biāo)示符key是kCATransition洒敏。(mac os)
2.創(chuàng)建action對(duì)象
layer調(diào)用actionForKey:
方法搜索需要執(zhí)行的action對(duì)象
龄恋。action對(duì)象
可以根據(jù)情況在不同的方法里被,具體情況如下按順序考慮:
(因?yàn)閘ayer搜索到一個(gè)action對(duì)象就會(huì)停止搜索桐玻。layer會(huì)根據(jù)下面順序優(yōu)先選擇靠前的方法)
1.如果設(shè)置了layer的代理篙挽,可以通過(guò)執(zhí)行代理方法actionForLayer:forKey:
返回一個(gè)action對(duì)象
。代理方法可以返回:
- 返回
action對(duì)象
,例如CAAnimation
對(duì)象镊靴。 - 返回
nil
铣卡。nil
表示結(jié)束actionForLayer:forKey:
方法的執(zhí)行,繼續(xù)搜索下一個(gè)階段偏竟。 - 返回
[NSNull null]
煮落。表示結(jié)束搜索,即結(jié)束actionForLayer:forKey:
踊谋,也結(jié)束其他階段蝉仇,將不會(huì)有隱式動(dòng)畫(huà)。
2.查找layer的actions
屬性殖蚕,看key是否有對(duì)應(yīng)的值轿衔。
3.查找layer的style
屬性。
4.layer調(diào)用defaultActionForKey:
方法睦疫。
5.如果搜索到了最后階段害驹,layer會(huì)執(zhí)行一個(gè)默認(rèn)的action對(duì)象
,一般是CABasicAnimation
蛤育。
上面方法具體選擇那種我也不知道宛官!
3.調(diào)用action對(duì)象的runActionForLayer:object:arguments:
方法執(zhí)行相關(guān)操作。
如果返回的是CAAnimation實(shí)例瓦糕,那么可以不實(shí)現(xiàn)runActionForLayer:object:arguments:
方法底洗,因?yàn)镃ore Animaiton已經(jīng)替你做好了CAAnimation的實(shí)現(xiàn)。
下面兩種方式是一樣的咕娄,一般我們都是使用CAAnimation亥揖,那么我們直接在actionForLayer
里實(shí)現(xiàn)我們想要的動(dòng)畫(huà)效果就行了,也就是代碼2:
代碼1:
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event{
if ([event isEqualToString:@"backgroundColor"]) {
MyAction *action = [MyAction new];
return action;
}
return nil;
}
@interface MyAction : NSObject<CAAction>
@end
@implementation MyAction
- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict{
CustomLayer *layer = anObject;
CABasicAnimation *animation = [CABasicAnimation animation];
animation.duration = 3.0f;
[layer addAnimation:animation forKey:@"backgroundColor"];
}
@end
代碼2:
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event{
if ([event isEqualToString:@"backgroundColor"]) {
CABasicAnimation *animation = [CABasicAnimation animation];
animation.duration = 3.0f;
[layer addAnimation:animation forKey:@"backgroundColor"];
return animation;
}
return nil;
}
那么為什么還有代碼1這種方式呢圣勒?后來(lái)我發(fā)現(xiàn)actionForLayer:
是在layer屬性值改變前調(diào)用的费变,,而action對(duì)象
的runActionForKey:
方法是在layer屬性值發(fā)生變化之后發(fā)生的灾而,比如我設(shè)置CABasicAnimation的fromValue
和toValue
的值胡控,就需要在action對(duì)象
里實(shí)現(xiàn):
@interface CircularProgressAction : NSObject<CAAction>
@property (assign , nonatomic) float oldValue;
@end
@implementation CircularProgressAction
- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict{
CircularProgress *layer = anObject;
CABasicAnimation * animation=[CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration=3;
animation.fromValue=[NSNumber numberWithFloat:self.oldValue/100.0];
animation.toValue=[NSNumber numberWithFloat:[[layer valueForKey:event] floatValue]/100.0];
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[layer addAnimation:animation forKey:@"strokeEnd"];
}
@end
通過(guò)改變CALayer的自定義屬性來(lái)產(chǎn)生自定義的默認(rèn)動(dòng)畫(huà)
代碼示例:
下面代碼通過(guò)改變CircularProgress
類(lèi)中的arcLenght
的值來(lái)產(chǎn)生動(dòng)畫(huà):
動(dòng)畫(huà)效果:
***************CircularProgress.h****************
#import <QuartzCore/QuartzCore.h>
@interface CircularProgress : CAShapeLayer
//1~100
@property (assign, nonatomic) float arcLenght;
- (instancetype)initWithFrame:(CGRect)frame;
@end
@interface CircularProgressAction : NSObject<CAAction>
@property (assign , nonatomic) float oldValue;
@end
************** CircularProgress.m******************
#import "CircularProgress.h"
#import <UIKit/UIKit.h>
@interface CircularProgress()<CALayerDelegate>
@end
@implementation CircularProgress
@dynamic arcLenght;
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super init]) {
[self setupLayers:frame];
}
return self;
}
- (void)setupLayers:(CGRect)frame{
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:CGPointMake(frame.size.width/2, frame.size.height/2) radius:50 startAngle:0 endAngle:2*M_PI clockwise:NO];
self.path = path.CGPath;
self.fillColor = [UIColor clearColor].CGColor;
self.strokeColor = [UIColor greenColor].CGColor;
self.lineWidth = 3;
self.delegate = self;
self.strokeStart = 0;
self.strokeEnd = 0;
}
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event{
CircularProgressAction *action = nil;
if ([event isEqualToString:@"arcLenght"]) {
action = [[CircularProgressAction alloc] init];
action.oldValue = self.arcLenght;
}
return action;
}
- (id<CAAction>)actionForKey:(NSString *)event{
return [super actionForKey:event];
}
@end
@implementation CircularProgressAction
- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict{
CircularProgress *layer = anObject;
CABasicAnimation * animation=[CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration=3;
animation.fromValue=[NSNumber numberWithFloat:self.oldValue/100.0];
animation.toValue=[NSNumber numberWithFloat:[[layer valueForKey:event] floatValue]/100.0];
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[layer addAnimation:animation forKey:@"strokeEnd"];
}
@end
*****************ViewController.m*****************
NSArray * colors = @[(id)[[self colorWithHex:0xFF6347] CGColor],
(id)[[self colorWithHex:0xFFEC8B] CGColor],
(id)[[self colorWithHex:0x98FB98] CGColor],
(id)[[self colorWithHex:0x00B2EE] CGColor],
(id)[[self colorWithHex:0x9400D3] CGColor]];
NSArray * locations = @[@0.1,@0.3,@0.5,@0.7,@1];
CAGradientLayer *gradientLayer = [CAGradientLayer new];
gradientLayer.frame = CGRectMake(100, 100, 200, 200);
gradientLayer.colors = colors;
gradientLayer.locations = locations;
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(1, 0);
[self.view.layer addSublayer:gradientLayer];
self.circularProgress = [[CircularProgress alloc] initWithFrame:gradientLayer.bounds];
gradientLayer.mask = self.circularProgress;
- (void)doRightButtonAction{
self.circularProgress.arcLenght = 50;
}
注意:
-
arcLenght
的值一定要有變化才能引起actionForKey :
進(jìn)行搜索。 -
arcLenght
需要用關(guān)鍵字@dynamic
修飾旁趟。 -
actionForKey :
和actionForLayer:
會(huì)在屬性值發(fā)生變化前調(diào)用昼激,而runActionForKey:
會(huì)在屬性值發(fā)生變化后調(diào)用庇绽,需要注意。
討論
關(guān)于actionForKey :
和actionForLayer:
兩個(gè)方法橙困,我不是很理解兩個(gè)的區(qū)別瞧掺,因?yàn)檫@兩個(gè)方法都是返回action對(duì)象
,而且actionForLayer:
需要設(shè)置代理。因此我在actionForKey :
里返回對(duì)應(yīng)的action對(duì)象
不是更好嗎凡傅?
當(dāng)然還有一個(gè)區(qū)別:如果在actionForKey :
里返回nil
或[NSNull null]
辟狈,那么搜素就會(huì)停止,而如果在actionForLayer:
里返回nil
會(huì)停止actionForLayer:
去搜素下一階段夏跷,返回[NSNull null]
才會(huì)停止搜索哼转。
取消隱式動(dòng)畫(huà)
你可以是用CATransaction
類(lèi)臨時(shí)取消隱式動(dòng)畫(huà)
[CATransaction begin];
[CATransaction setDisableActions:YES];
NSInteger x = arc4random() % 100;
self.circularProgress.arcLenght = x;
[CATransaction commit];
設(shè)置setDisableActions:
為YES后,layer的actionForKey:
方法將不會(huì)被調(diào)用槽华,隱式動(dòng)畫(huà)也不會(huì)生成壹蔓。