CALayer的隱式動(dòng)畫(huà)

我們修改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的fromValuetoValue的值胡控,就需要在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;
}

注意:

  1. arcLenght的值一定要有變化才能引起actionForKey :進(jìn)行搜索。
  2. arcLenght需要用關(guān)鍵字@dynamic修飾旁趟。
  3. 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ì)生成壹蔓。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市猫态,隨后出現(xiàn)的幾起案子佣蓉,更是在濱河造成了極大的恐慌,老刑警劉巖亲雪,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勇凭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡义辕,警方通過(guò)查閱死者的電腦和手機(jī)虾标,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)终息,“玉大人夺巩,你說(shuō)我怎么就攤上這事贞让≈苷福” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵喳张,是天一觀(guān)的道長(zhǎng)续镇。 經(jīng)常有香客問(wèn)我,道長(zhǎng)销部,這世上最難降的妖魔是什么摸航? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮舅桩,結(jié)果婚禮上酱虎,老公的妹妹穿的比我還像新娘。我一直安慰自己擂涛,他們只是感情好读串,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般恢暖。 火紅的嫁衣襯著肌膚如雪排监。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天杰捂,我揣著相機(jī)與錄音舆床,去河邊找鬼。 笑死嫁佳,一個(gè)胖子當(dāng)著我的面吹牛挨队,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蒿往,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼瞒瘸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了熄浓?” 一聲冷哼從身側(cè)響起情臭,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赌蔑,沒(méi)想到半個(gè)月后俯在,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娃惯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年跷乐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趾浅。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡愕提,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出皿哨,到底是詐尸還是另有隱情浅侨,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布证膨,位于F島的核電站如输,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏央勒。R本人自食惡果不足惜不见,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崔步。 院中可真熱鬧稳吮,春花似錦、人聲如沸井濒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至喻奥,卻和暖如春席纽,著一層夾襖步出監(jiān)牢的瞬間撞蚕,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留刀疙,地道東北人谦秧。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓疚鲤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親桶略。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诲宇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容