iOS-UIView和CALayer的關(guān)系

1.響應(yīng)事件

首先從繼承關(guān)系來(lái)看,UIView繼承于UIResponse,而CALayer繼承于NSObject。UIKit使用UIResponse作為響應(yīng)對(duì)象,來(lái)響應(yīng)系統(tǒng)傳遞的事件并進(jìn)行處理。所以UIView可以響應(yīng)事件,而CALayer不具備響應(yīng)事件的能力。CALayer是QuartzCore中的類,負(fù)責(zé)繪制內(nèi)容。

下面列舉一些處理觸摸事件的接口:

-(void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(nullable UIEvent*)event

-(void)touchesMoved:(NSSet<UITouch*>*)touches withEvent:(nullable UIEvent*)event

-(void)touchesEnded:(NSSet<UITouch*>*)touches withEvent:(nullable UIEvent*)event

-(void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(nullable UIEvent*)event

-(void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch*>*)touchesNS_AVAILABLE_IOS(9_1)

并且UIView中提供了以下兩個(gè)方法,來(lái)進(jìn)行iOS中事件的響應(yīng)和傳遞:

-(nullable UIView*)hitTest:(CGPoint)point withEvent:(nullable UIEvent*)event

-(BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent*)event

2.初始化和Frame

一個(gè) Layer 的 frame 是由它的 anchorPoint,position,bounds,和 transform 共同決定的,而一個(gè) View 的 frame 只是簡(jiǎn)單的返回 Layer的 frame,同樣 View 的 center和 bounds 也是返回 Layer 的一些屬性。為了一探究竟,做如下測(cè)試。

自定義兩個(gè)類MyView和MyLayer,分別繼承于UIView和CALayer。

在MyView中重寫以下方法:

- (instancetype)init {

? ? self= [superinit];

? ? if(self) {

? ? ? ? NSLog(@"============ MyView init!");

? ? }

? ? return self;

}

+ (Class)layerClass {

? ? return[MyLayerclass];

}

- (void)setFrame:(CGRect)frame {

? ? [supersetFrame:frame];

}

- (void)setCenter:(CGPoint)center {

? ? [supersetCenter:center];

}

- (void)setBounds:(CGRect)bounds {

? ? [supersetBounds:bounds];

}

在MyLayer中重寫以下方法:

- (instancetype)init

{

? ? self= [super init];

? ? if(self) {

? ? ? ? NSLog(@"============= MyLayer init!");

? ? }

? ? return self;

}

+ (Class)layerClass {

? ? return [MyLayer class];

}

- (void)setFrame:(CGRect)frame {

? ? [super setFrame:frame];

}

- (void)setPosition:(CGPoint)position {

? ? [super setPosition:position];

}

- (void)setBounds:(CGRect)bounds {

? ? [super setBounds:bounds];

}

在兩個(gè)類的初始化方法中都打下斷點(diǎn)調(diào)用:

可以看到在創(chuàng)建MyView時(shí)會(huì)調(diào)用私有方法 [UIView _createLayerWithFrame:] 創(chuàng)建CALayer。然后在創(chuàng)建View時(shí)在View和Layer的Frame相關(guān)方法中都加上斷點(diǎn),可以看到調(diào)用順序如下:

[MyLayer setBounds:]

[MyView setFrame:]

[MyLayer setFrame:]

[MyLayer setPosition:]

[MyLayer setBounds:]

由此看到創(chuàng)建時(shí)只調(diào)用了Layer的設(shè)置尺寸和位置,并沒有調(diào)用View的setCenter:和setBounds:方法。

然后我發(fā)現(xiàn)當(dāng)我修改了 view的bounds.size或者bounds.origin的時(shí)候也只會(huì)調(diào)用上邊 Layer的一些方法。所以我大膽的猜一下,View 的 Center 和 Bounds 只是直接返回layer 對(duì)應(yīng)的 Position 和 Bounds.

View中frame getter方法,bounds和center,UIView并沒有做什么工作;它只是簡(jiǎn)單的各自調(diào)用它底層的CALayer的frame,bounds和position方法。

3.UIView主要是對(duì)顯示內(nèi)容的管理而CALayer負(fù)責(zé)顯示內(nèi)容的繪制

在UIView和CALayer分別重寫父類方法:

在MyView重寫drawRect:

- (void)drawRect:(CGRect)rect {

? ? [super drawRect:rect];

}

在MyLayer重寫display:

- (void)display {

? ? [super display];

}

在兩個(gè)方法中打斷點(diǎn)并執(zhí)行,得到如下結(jié)果:


可以看到UIView是CALayer的CALayerDelegate,由此可以推測(cè)是在代理方法內(nèi)部[UIView(CALayerDelegate) drawLayer:inContext]調(diào)用UIView的drawRect方法,從而繪制出了UIView的內(nèi)容。

4.隱式動(dòng)畫

每個(gè)view都有一個(gè)layer,但是也有一些不依附view單獨(dú)存在的layer,如CAShapelayer。它們不需要附加到 view 上就可以在屏幕上顯示內(nèi)容。

基本上你改變一個(gè)單獨(dú)的 layer 的任何屬性的時(shí)候,都會(huì)觸發(fā)一個(gè)從舊的值過渡到新值的簡(jiǎn)單動(dòng)畫(這就是所謂的隱式動(dòng)畫)。然而,如果你改變的是 view 中 layer 的同一個(gè)屬性,它只會(huì)從這一幀直接跳變到下一幀。盡管兩種情況中都有 layer,但是當(dāng) layer 附加在 view 上時(shí),它的默認(rèn)的隱式動(dòng)畫的 layer 行為就不起作用了。

在 Core Animation 編程指南的 “How to Animate Layer-Backed Views” 中,對(duì)為什么會(huì)這樣做出了一個(gè)解釋:

UIView默認(rèn)情況下禁止了layer動(dòng)畫,但是在animation block中又重新啟用了它們。

是因?yàn)槿魏慰蓜?dòng)畫的 layer 屬性改變時(shí),layer都會(huì)尋找并運(yùn)行合適的action來(lái)實(shí)行這個(gè)改變。在Core Animation的專業(yè)術(shù)語(yǔ)中就把這樣的動(dòng)畫統(tǒng)稱為動(dòng)作 (action,或者CAAction)。

layer通過向它的delegate發(fā)送actionForLayer:forKey:消息來(lái)詢問提供一個(gè)對(duì)應(yīng)屬性變化的action。delegate可以通過返回以下三者之一來(lái)進(jìn)行響應(yīng):

它可以返回一個(gè)動(dòng)作對(duì)象,這種情況下layer將使用這個(gè)動(dòng)作。

它可以返回一個(gè)nil, 這樣layer就會(huì)到其他地方繼續(xù)尋找。

它可以返回一個(gè)NSNull對(duì)象,告訴layer這里不需要執(zhí)行一個(gè)動(dòng)作,搜索也會(huì)就此停止。

當(dāng)layer在背后支持一個(gè)view的時(shí)候,view就是它的delegate。

5.單一職責(zé)

UIView負(fù)責(zé)用戶的響應(yīng)操作,CALayer負(fù)責(zé)繪制,就像一個(gè)類似公司的框架一樣,把如同公司職員的各個(gè)功能層級(jí)組織起來(lái),然后各司其職。

機(jī)制與策略分離

Unix內(nèi)核設(shè)計(jì)的一個(gè)主要思想是——提供(Mechanism)機(jī)制而不是策略(Policy)。編程問題都可以抽離出機(jī)制和策略部分。機(jī)制一旦實(shí)現(xiàn),就會(huì)很少更改,但策略會(huì)經(jīng)常得到優(yōu)化。例如原子可以看做是機(jī)制,而各種原子的組成就是一種策略。CALayer也可以看做是一種機(jī)制,提供圖層繪制,你們可以翻開CALayer的頭文件看看,基本上是沒怎么變過的,而UIView可以看做是策略,變動(dòng)很多。越是底層,越是機(jī)制,越是機(jī)制就越是穩(wěn)定。機(jī)制與策略分離,可以使得需要修改的代碼更少,特別是底層代碼,這樣可以提高系統(tǒng)的穩(wěn)定性。

更多的不可變

穩(wěn)定給你的是什么感覺?堅(jiān)固?不可形變?穩(wěn)定其實(shí)就是不可變。一個(gè)系統(tǒng)不可變的東西越多,越是穩(wěn)定。所以機(jī)制恰是滿足這個(gè)不可變的因素的。構(gòu)建一個(gè)系統(tǒng)有一個(gè)指導(dǎo)思想就是盡量抽取不可變的東西和可變的東西分離。水是成不了萬(wàn)丈高樓的,堅(jiān)固的混凝土才可以。更少的修改,意味著更少的bug的幾率。

各司其職

即使能力再大也不能把說有事情都干了,萬(wàn)一哪一天不行了呢,那就是突然什么都不能干了。所以僅僅是基于分散風(fēng)險(xiǎn)原則也不應(yīng)該出現(xiàn)全能類。各司其職,相互合作,把可控粒度降到最低,這樣也可以是系統(tǒng)更穩(wěn)定,更易修改。

漏的更少

接口應(yīng)該面向大眾的,按照八二原則,其實(shí)20%的接口就可以滿足80%的需求,剩下的80%應(yīng)該隱藏在背后。因?yàn)槁┑纳倏偸前踩模皇菃?。剩下?0%專家接口可以隱藏與深層次。比如UIView遮蔽了大部分的CALayer接口,抽取構(gòu)造出更易用的frame和動(dòng)畫實(shí)現(xiàn),這樣上手更容易。

總結(jié):

1.view負(fù)責(zé)了與人的動(dòng)作交互以及對(duì)layer的管理,layer則負(fù)責(zé)了所有能讓人看到的東西。

2.每個(gè) UIView 內(nèi)部都有一個(gè) CALayer 在背后提供內(nèi)容的繪制和顯示,并且 UIView 的尺寸樣式都由內(nèi)部的 Layer 所提供。兩者都有樹狀層級(jí)結(jié)構(gòu),layer 內(nèi)部有SubLayers,View 內(nèi)部有SubViews.但是 Layer 比 View 多了個(gè)AnchorPoint。

3.在 View顯示的時(shí)候,UIView 做為 Layer 的CALayerDelegate,View 的顯示內(nèi)容取決于內(nèi)部的 CALayer 的?display。

4.CALayer 是默認(rèn)修改屬性支持隱式動(dòng)畫的,在給 UIView 的 Layer 做動(dòng)畫的時(shí)候,View 作為 Layer 的代理,Layer 通過actionForLayer:forKey:向 View請(qǐng)求相應(yīng)的action(動(dòng)畫行為)。

5.layer 內(nèi)部維護(hù)著三分layer tree,分別是presentLayer Tree(動(dòng)畫樹),modeLayer Tree(模型樹),Render Tree(渲染樹),在做 iOS動(dòng)畫的時(shí)候,我們修改動(dòng)畫的屬性,在動(dòng)畫的其實(shí)是 Layer 的 presentLayer的屬性值,而最終展示在界面上的其實(shí)是提供 View的modelLayer。

6.單一職責(zé)的設(shè)計(jì)。

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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