#前言
文章之前先問個小題目吧:
如下圖,點選螢幕時,紅色View的center修改為點選的位置,紅色View移動過程是否有動畫?如果有,動畫時間是多少?
如果你是個老司機,能回答這道題,並深知其底層原理,那就別浪費時間了,趕快離開吧。 文章最後給出答案,想好了翻到文章最後檢視答案。
##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] 的列印結果是尖括號