iOS面試題:UIView block動(dòng)畫(huà)實(shí)現(xiàn)原理

在了解UIView block動(dòng)畫(huà)實(shí)現(xiàn)原理之前,需要先了解CALayer的可動(dòng)畫(huà)屬性。

1. CALayer的可動(dòng)畫(huà)屬性

CALayer擁有大量的屬性,看CALayer的頭文件內(nèi)容,會(huì)發(fā)現(xiàn)很多的屬性的注釋中,最后會(huì)有一個(gè)詞叫做Animatable,直譯過(guò)來(lái)是可動(dòng)畫(huà)的。下面的截圖只是CALayer眾多可動(dòng)畫(huà)屬性中的一部分(注意frame并不是可動(dòng)畫(huà)的屬性)

/* The bounds of the layer. Defaults to CGRectZero. Animatable. */

@property CGRect bounds;

/* The position in the superlayer that the anchor point of the layer's
 * bounds rect is aligned to. Defaults to the zero point. Animatable. */

@property CGPoint position;

/* The Z component of the layer's position in its superlayer. Defaults
 * to zero. Animatable. */

@property CGFloat zPosition;

/* Defines the anchor point of the layer's bounds rect, as a point in
 * normalized layer coordinates - '(0, 0)' is the bottom left corner of
 * the bounds rect, '(1, 1)' is the top right corner. Defaults to
 * '(0.5, 0.5)', i.e. the center of the bounds rect. Animatable. */

@property CGPoint anchorPoint;

/* The Z component of the layer's anchor point (i.e. reference point for
 * position and transform). Defaults to zero. Animatable. */

@property CGFloat anchorPointZ;

/* A transform applied to the layer relative to the anchor point of its
 * bounds rect. Defaults to the identity transform. Animatable. */

@property CATransform3D transform;

如果一個(gè)屬性被標(biāo)記為Animatable,那么它具有以下兩個(gè)特點(diǎn):

1、直接對(duì)它賦值可能產(chǎn)生隱式動(dòng)畫(huà);
2、我們的CAAnimation的keyPath可以設(shè)置為這個(gè)屬性的名字。

當(dāng)我們直接對(duì)可動(dòng)畫(huà)屬性賦值的時(shí)候,由于有隱式動(dòng)畫(huà)存在的可能,CALayer首先會(huì)判斷此時(shí)有沒(méi)有隱式動(dòng)畫(huà)被觸發(fā)。它會(huì)讓它的delegate(沒(méi)錯(cuò)CALayer擁有一個(gè)屬性叫做delegate)調(diào)用actionForLayer:forKey:來(lái)獲取一個(gè)返回值,這個(gè)返回值在聲明的時(shí)候是一個(gè)id對(duì)象,當(dāng)然在運(yùn)行時(shí)它可能是任何對(duì)象。這時(shí)CALayer拿到返回值,將進(jìn)行判斷:

  • 如果返回的對(duì)象是一個(gè)nil,則進(jìn)行默認(rèn)的隱式動(dòng)畫(huà);
  • 如果返回的對(duì)象是一個(gè)[NSNull null] ,則CALayer不會(huì)做任何動(dòng)畫(huà);
  • 如果是一個(gè)正確的實(shí)現(xiàn)了CAAction協(xié)議的對(duì)象,則CALayer用這個(gè)對(duì)象來(lái)生成一個(gè)CAAnimation,并加到自己身上進(jìn)行動(dòng)畫(huà)。

理解完這些,我們?cè)賮?lái)分析UIView的block動(dòng)畫(huà)就容易理解了。

2. UIView的block動(dòng)畫(huà)
Amazing things happen when they are in a block.

有趣的是,如果這個(gè)CALayer被一個(gè)UIView所持有,那么這個(gè)CALayer的delegate就是持有它的那個(gè)UIView。

大家應(yīng)該可以思考出這樣的問(wèn)題:為什么同樣的一行代碼在block里面就有動(dòng)畫(huà)在block外面就沒(méi)動(dòng)畫(huà),就像下面這樣:

/** 產(chǎn)生動(dòng)畫(huà) */
- (void)createAnimation {
    UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];

    // 這樣寫(xiě)沒(méi)有動(dòng)畫(huà)
    redView.center = CGPointMake(200, 300);

    [UIView animateWithDuration:1.25 animations:^{
        // 寫(xiě)在block里面就有動(dòng)畫(huà)
        redView.center = CGPointMake(200, 300);
    }];
}

既然UIView就是CALayer的delegate,那么actionForLayer:forKey:方法就是由UIView來(lái)實(shí)現(xiàn)的。所以UIView可以相當(dāng)靈活的控制動(dòng)畫(huà)的產(chǎn)生。

當(dāng)我們對(duì)UIView的一個(gè)屬性賦值的時(shí)候,它只是簡(jiǎn)單的調(diào)用了它持有的那個(gè)CALayer的對(duì)應(yīng)的屬性的setter方法而已,根據(jù)上面的可動(dòng)畫(huà)屬性的特點(diǎn),CALayer會(huì)讓它的delegate(也就是這個(gè)UIView)調(diào)用actionForLayer:forKey:方法。實(shí)際上結(jié)果大家都應(yīng)該能想得到:在UIView的動(dòng)畫(huà)block外面,UIView的這個(gè)方法將返回NSNull,而在block里面,UIView將返回一個(gè)正確的CAAction對(duì)象(這里將不深究UIView是如何判斷此時(shí)setter的調(diào)用是在動(dòng)畫(huà)block外面還是里面的)。

為了證明這個(gè)結(jié)論,我們將繼續(xù)進(jìn)行實(shí)驗(yàn):

/** UIView 動(dòng)畫(huà)產(chǎn)生原理 */
- (void)uiviewAnimation {
    NSLog(@"%@",[self.view.layer.delegate actionForLayer:self.view.layer forKey:@"position"]);

    [UIView animateWithDuration:1.25 animations:^{
        NSLog(@"%@",[self.view.layer.delegate actionForLayer:self.view.layer forKey:@"position"]);
    }];
}

我們分別在block外面和block里面打印actionForLayer:forKey:方法的返回值,看看它究竟是什么玩意。

2019-06-11 19:45:23.973002+0800 YaZhaiInterviewQuestionDemo[93569:1953578] <null>
2019-06-11 19:45:23.973622+0800 YaZhaiInterviewQuestionDemo[93569:1953578] <_UIViewAdditiveAnimationAction: 0x600001ff0d40>

打印發(fā)現(xiàn),我們的結(jié)論是正確的:在block外面,這個(gè)方法將返回一個(gè)NSNull(是尖括號(hào)的null,nil打印出來(lái)是圓括號(hào)的null),而在block里面返回了一個(gè)叫做UIViewAdditiveAnimationAction類(lèi)的對(duì)象,這個(gè)類(lèi)是一個(gè)私有類(lèi),遵循了蘋(píng)果一罐的命名規(guī)范: xxAction,一定就是一個(gè)實(shí)現(xiàn)了CAAction協(xié)議的對(duì)象了。

這也就說(shuō)明了為什么我們對(duì)一個(gè)view的center賦值,如果這行代碼在動(dòng)畫(huà)block里面,就會(huì)有動(dòng)畫(huà),在block外面則沒(méi)有動(dòng)畫(huà)。


更多:iOS面試題合集

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,634評(píng)論 1 32
  • 問(wèn)題: 1.什么是隱式動(dòng)畫(huà),為什么CALayer設(shè)置可動(dòng)畫(huà)屬性時(shí)會(huì)觸發(fā)隱式動(dòng)畫(huà)?2.UIView設(shè)置屬性,為什么沒(méi)...
    人生看淡不服就干閱讀 10,856評(píng)論 1 15
  • 前言 本文只要描述了iOS中的Core Animation(核心動(dòng)畫(huà):隱式動(dòng)畫(huà)、顯示動(dòng)畫(huà))、貝塞爾曲線、UIVie...
    GitHubPorter閱讀 3,736評(píng)論 7 11
  • 目錄 UIWindow,UIView,CALayer的區(qū)別事件傳遞和響應(yīng)機(jī)制UIView block動(dòng)畫(huà)實(shí)現(xiàn)原理 ...
    路飛_Luck閱讀 1,865評(píng)論 6 26
  • 前言 上一章中我們深入研究了UIView和它持有的那個(gè)CALayer之間的關(guān)系,知道了我們對(duì)UIView的各種屬性...
    hehc08閱讀 2,578評(píng)論 0 4

友情鏈接更多精彩內(nèi)容