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中的類(lèi)擒权,負(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è)類(lèi)MyView和MyLayer焰盗,分別繼承于UIView和CALayer贱鄙。
在MyView中重寫(xiě)以下方法:
- (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中重寫(xiě)以下方法:
- (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è)類(lèi)的初始化方法中都打下斷點(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è)置尺寸和位置逗宁,并沒(méi)有調(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并沒(méi)有做什么工作哼拔;它只是簡(jiǎn)單的各自調(diào)用它底層的CALayer的frame引有,bounds和position方法。
3.UIView主要是對(duì)顯示內(nèi)容的管理而CALayer負(fù)責(zé)顯示內(nèi)容的繪制
在UIView和CALayer分別重寫(xiě)父類(lèi)方法:
在MyView重寫(xiě)drawRect:
- (void)drawRect:(CGRect)rect {
? ? [super drawRect:rect];
}
在MyLayer重寫(xiě)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)畫(huà)
每個(gè)view都有一個(gè)layer曾我,但是也有一些不依附view單獨(dú)存在的layer,如CAShapelayer健民。它們不需要附加到 view 上就可以在屏幕上顯示內(nèi)容抒巢。
基本上你改變一個(gè)單獨(dú)的 layer 的任何屬性的時(shí)候,都會(huì)觸發(fā)一個(gè)從舊的值過(guò)渡到新值的簡(jiǎn)單動(dòng)畫(huà)(這就是所謂的隱式動(dòng)畫(huà))秉犹。然而蛉谜,如果你改變的是 view 中 layer 的同一個(gè)屬性,它只會(huì)從這一幀直接跳變到下一幀崇堵。盡管兩種情況中都有 layer型诚,但是當(dāng) layer 附加在 view 上時(shí),它的默認(rèn)的隱式動(dòng)畫(huà)的 layer 行為就不起作用了鸳劳。
在 Core Animation 編程指南的 “How to Animate Layer-Backed Views” 中俺驶,對(duì)為什么會(huì)這樣做出了一個(gè)解釋?zhuān)?/p>
UIView默認(rèn)情況下禁止了layer動(dòng)畫(huà),但是在animation block中又重新啟用了它們棍辕。
是因?yàn)槿魏慰蓜?dòng)畫(huà)的 layer 屬性改變時(shí)暮现,layer都會(huì)尋找并運(yùn)行合適的action來(lái)實(shí)行這個(gè)改變。在Core Animation的專(zhuān)業(yè)術(shù)語(yǔ)中就把這樣的動(dòng)畫(huà)統(tǒng)稱(chēng)為動(dòng)作 (action楚昭,或者CAAction)栖袋。
layer通過(guò)向它的delegate發(fā)送actionForLayer:forKey:消息來(lái)詢問(wèn)提供一個(gè)對(duì)應(yīng)屬性變化的action。delegate可以通過(guò)返回以下三者之一來(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è)類(lèi)似公司的框架一樣娱局,把如同公司職員的各個(gè)功能層級(jí)組織起來(lái),然后各司其職咧七。
機(jī)制與策略分離
Unix內(nèi)核設(shè)計(jì)的一個(gè)主要思想是——提供(Mechanism)機(jī)制而不是策略(Policy)衰齐。編程問(wèn)題都可以抽離出機(jī)制和策略部分。機(jī)制一旦實(shí)現(xiàn)继阻,就會(huì)很少更改耻涛,但策略會(huì)經(jīng)常得到優(yōu)化。例如原子可以看做是機(jī)制瘟檩,而各種原子的組成就是一種策略抹缕。CALayer也可以看做是一種機(jī)制,提供圖層繪制芒帕,你們可以翻開(kāi)CALayer的頭文件看看歉嗓,基本上是沒(méi)怎么變過(guò)的丰介,而UIView可以看做是策略背蟆,變動(dòng)很多。越是底層哮幢,越是機(jī)制带膀,越是機(jī)制就越是穩(wěn)定。機(jī)制與策略分離橙垢,可以使得需要修改的代碼更少垛叨,特別是底層代碼,這樣可以提高系統(tǒng)的穩(wěn)定性柜某。
更多的不可變
穩(wěn)定給你的是什么感覺(jué)嗽元?堅(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的幾率裁奇。
各司其職
即使能力再大也不能把說(shuō)有事情都干了桐猬,萬(wàn)一哪一天不行了呢,那就是突然什么都不能干了刽肠。所以僅僅是基于分散風(fēng)險(xiǎn)原則也不應(yīng)該出現(xiàn)全能類(lèi)课幕。各司其職厦坛,相互合作,把可控粒度降到最低乍惊,這樣也可以是系統(tǒng)更穩(wěn)定杜秸,更易修改。
漏的更少
接口應(yīng)該面向大眾的润绎,按照八二原則撬碟,其實(shí)20%的接口就可以滿足80%的需求,剩下的80%應(yīng)該隱藏在背后莉撇。因?yàn)槁┑纳倏偸前踩哪馗颍皇菃帷JO碌?0%專(zhuān)家接口可以隱藏與深層次棍郎。比如UIView遮蔽了大部分的CALayer接口其障,抽取構(gòu)造出更易用的frame和動(dòng)畫(huà)實(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 所提供汽抚。兩者都有樹(shù)狀層級(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)畫(huà)的惭蟋,在給 UIView 的 Layer 做動(dòng)畫(huà)的時(shí)候,View 作為 Layer 的代理药磺,Layer 通過(guò)actionForLayer:forKey:向 View請(qǐng)求相應(yīng)的action(動(dòng)畫(huà)行為)告组。
5.layer 內(nèi)部維護(hù)著三分layer tree,分別是presentLayer Tree(動(dòng)畫(huà)樹(shù)),modeLayer Tree(模型樹(shù)),Render Tree(渲染樹(shù)),在做 iOS動(dòng)畫(huà)的時(shí)候,我們修改動(dòng)畫(huà)的屬性与涡,在動(dòng)畫(huà)的其實(shí)是 Layer 的 presentLayer的屬性值,而最終展示在界面上的其實(shí)是提供 View的modelLayer惹谐。
6.單一職責(zé)的設(shè)計(jì)。