iOS CoreAnimation 隱式動畫

Jaly_xxx發表於2018-01-03

#前言

文章之前先問個小題目吧:

如下圖,點選螢幕時,紅色View的center修改為點選的位置,紅色View移動過程是否有動畫?如果有,動畫時間是多少?

redView.png

如果你是個老司機,能回答這道題,並深知其底層原理,那就別浪費時間了,趕快離開吧。 文章最後給出答案,想好了翻到文章最後檢視答案。

##1、要弄清楚這個小題目,就需要研究CoreAnimation的底層原理了 Core Animation通常對CALayer的可動畫屬性做動畫,如果將題中的View換成CALayer,將會有動畫。CALayer 屬於 QuartzCore 框架, UIView 屬於 UIKit 框架。UIKit建立在Core Animation之上,隱式動畫如何被 UIKit 禁用掉的呢?

1、修改layer的動畫屬性時,會呼叫CALayer的一個方法,搜尋一個動作

/* Returns the action object associated with the event named by the
 * string 'event'. The default implementation searches for an action
 * object in the following places:
 *
 * 1. if defined, call the delegate method -actionForLayer:forKey:
 * 2. look in the layer's `actions' dictionary
 * 3. look in any `actions' dictionaries in the `style' hierarchy
 * 4. call +defaultActionForKey: on the layer's class
 *
 * If any of these steps results in a non-nil action object, the
 * following steps are ignored. If the final result is an instance of
 * NSNull, it is converted to `nil'. */

- (nullable id<CAAction>)actionForKey:(NSString *)event;
複製程式碼

2、如果這個方法返回一個實現了CAAction協議的物件,返回的物件將會呼叫CAAction協議的一個方法

/* Called to trigger the event named 'path' on the receiver. The object
 * (e.g. the layer) on which the event happened is 'anObject'. The
 * arguments dictionary may be nil, if non-nil it carries parameters
 * associated with the event. */

- (void)runActionForKey:(NSString *)event object:(id)anObject
    arguments:(nullable NSDictionary *)dict;

複製程式碼

3、上面方法中 anObject 是個 CALayer,anObject 呼叫
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;方法,animation會拷貝一份新增到CALayer上,用於產生動畫。

#2動作搜尋 上面第一步,搜尋動作的過程比較複雜, - (nullable id<CAAction>)actionForKey:(NSString *)event;方法搜尋動作的步驟是:

1、檢測 CALayer 的 delegate ,看看delegate是否實現 -actionForLayer:forKey: 方法,如果有delegate,並且實現了此方法,那就直接呼叫並返回結果。 2、如果沒有delegate,或沒有實現 -actionForLayer:forKey: 方法,就檢測屬性名對應動作的 actions 字典。 3、如果 actions 字典沒有對應的屬性,就搜尋 style 字典 4、如果 style 字典也沒有,就呼叫+ (nullable id)defaultActionForKey:(NSString *)event 方法。

以上任何一步,如果返回非 nil,則搜尋停止,否則繼續往下搜尋。 經過一輪搜尋之後,- actionForKey:(NSString *);要麼返回nil 不會有動畫,要麼是CAAction協議的物件,CALayer 拿這個物件做動畫。但有一種特殊情況,就是返回 [NSNull null] ,由於是非 nil,則搜尋停止, [NSNull null] 也會轉化為 nil,導致沒有動畫的結果。

UIView中,layer的delegate指向UIView,UIView的-actionForLayer:forKey:方法預設返回 [NSNull null],在Block動畫中返回 CAAnimation 物件,所以UIView的隱式動畫才能被禁止。可以通過下面的程式碼來證明:

    NSLog(@"%@   %@", nil ,[NSNull null]);
    NSLog(@"%@", [self.redView actionForLayer:self.redView.layer forKey:@"backgroundColor"]);
    

    [UIView animateWithDuration:0.1 animations:^{
        NSLog(@"%@", [self.redView actionForLayer:self.redView.layer forKey:@"backgroundColor"]);
    }];
複製程式碼

列印結果:

(null) <CABasicAnimation: 0x60800003b8c0> 注意: [NSNull null] 的列印結果是尖括號


文章開頭問題答案

最後回答文章開頭的問題:沒有動畫,UIView禁用了隱式動畫,具體原因和原理檢視文章吧。

實驗 demo

相關文章