今天單純說一下CALayer和UIView之間的關(guān)系,反正宗旨就是隨意。
UIView和CALayer的類定義
點擊UIView進去可以看到UIView的定義如下,關(guān)鍵要注意的是CALayer,從代碼中可以看出UIView內(nèi)部定義了一個CALayer對象
。
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace>
+ (Class)layerClass; // default is [CALayer class]. Used when creating the underlying layer for the view.
- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
@property(nonatomic,getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default is YES. if set to NO, user events (touch, keys) are ignored and removed from the event queue.
@property(nonatomic) NSInteger tag; // default is 0
@property(nonatomic,readonly,strong) CALayer *layer; // returns view's layer. Will always return a non-nil value. view is layer's delegate
+ (UIUserInterfaceLayoutDirection)userInterfaceLayoutDirectionForSemanticContentAttribute:(UISemanticContentAttribute)attribute NS_AVAILABLE_IOS(9_0);
@property (nonatomic) UISemanticContentAttribute semanticContentAttribute NS_AVAILABLE_IOS(9_0);
@end
繼續(xù)點進去CALayer,可以看到CALayer的定義,里面有一系列的屬性和方法的定義。可以看到CALayer定義里面有對層次操作的屬性和方法如下:
@property(nullable, copy) NSArray<CALayer *> *sublayers
@property(nullable, readonly) CALayer *superlayer```
- (void)addSublayer:(CALayer *)layer
- (void)removeFromSuperlayer
- (void)insertSublayer:(CALayer *)layer atIndex:(unsigned)idx
- (void)insertSublayer:(CALayer *)layer below:(nullable CALayer *)sibling
- (void)insertSublayer:(CALayer *)layer above:(nullable CALayer *)sibling
- (void)replaceSublayer:(CALayer *)layer with:(CALayer *)layer2```
稍微有經(jīng)驗的iOS開發(fā)者肯定會覺得這些方法在UIView是曾相識。
UIView的對層次操作的屬性和方法如下:
@property(nullable, nonatomic,readonly) UIView *superview
@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *subviews```
- (void)addSubview:(UIView *)view
- (void)removeFromSuperview
- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview
- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2```
對于UIView的方法相信大部分讀者都非常熟悉,主要是用來操作UIView樹。
什么是UIView樹?可以閱讀組合模式,一棵UIView樹看起來大概是這樣的
Paste_Image.png
比如你要添加一個UIView到一另個UIView上,你會這么做:
view1.addSubView(view2)```
這么就可以把view2添加到view1的上面。至于效果相信讀者也是很了解和明白。
那么對于CALayer來說,其實也是“同樣”方法和效果,可以這么做:
layer1.addSublayer(layer2)```
至于效果讀者可以理解為跟view1.addSubView(view2)
一樣,把layer2加到了layer1上面,用戶就可以看到layer2在layer1上面了。
那么問題來了,既然UIView和CALayer這些方法和屬性達到的效果都“相同”,那為什么要區(qū)分開兩個出來?他們之間是什么關(guān)系呢?
UIView和CALayer的關(guān)系
要理清他們之間的關(guān)系得先從MVC模式開始說起,相信絕大多數(shù)的讀者都知道MVC是什么東西,理解比較深刻的會從他們是要解決耦合的問題開始說起,理解比較簡單的就說M就是模型,V就是視圖,C就是控制器。這些說法都沒錯。
什么是MVC?可以閱讀MVC
其實說白了UIView和CALayer之間的關(guān)系就是MVC的關(guān)系,UIView就是C,CALayer就是M,負責(zé)繪圖單元的籠統(tǒng)來說就是V。
什么是繪圖單元?可以閱讀像素繪制
籠統(tǒng)的說包括:CPU,GPU,屏幕等,一起來完成把圖片顯示到屏幕工作的單元。
當(dāng)要顯示像素到屏幕上,他們大概是這么工作的:
①當(dāng)繪圖單元需要繪制CALayer的時候,會拿到被標(biāo)記為需要繪制的CALayer渲染樹的值,以及要顯示圖片,進行像素合成。
②CALayer自身有個delegate,設(shè)置的是UIView, 當(dāng)CALayer被繪制時會執(zhí)行delegate方法通知UIView,看看UIView是有提供需要繪制的元素。
③如果UIView什么都不需要提供,就當(dāng)作無視。
CALayer如何通過delegate通知UIView?需要提供的元素是什么?可以閱讀CALayer基礎(chǔ)
當(dāng)你修改代碼之后,他們大概是這么工作的:
①當(dāng)你修改UIView的frame.size.width之后,UIView實際上是會把你修改的值映射到CALayer上去,讓CALayer知道修改了什么。
②CALayer獲取到要修改的值,然后去更新CALayer的模型樹,呈現(xiàn)樹,渲染樹,準(zhǔn)備好了渲染樹。
③等繪制單元要繪制到這個CALayer的時候就會按照渲染樹的值渲染到屏幕去。
什么是CALayer的模型樹,呈現(xiàn)樹,渲染樹?可以閱讀CALayer基礎(chǔ)
好了,看到這里,相信讀者也明白UIView跟CALayer之間就是MVC的關(guān)系了,那么為什么蘋果要把他們弄成MVC的關(guān)系,那就是因為遵從單一職責(zé)原則,那么問題又來了CALayer有什么職責(zé)?從MVC來看,CALayer屬于M,那么他的職責(zé)就是存儲數(shù)據(jù),那明顯的UIView屬于C,他的職責(zé)對于CALayer來說就是管理。
什么是單一職責(zé)原則?遵從單一職責(zé)原則有什么好處?可以閱讀設(shè)計原則
用一句話來說,單一職責(zé)原則就是,只做他自己關(guān)心的事情。
還有一個問題沒解決,回到文章最上面UIView和CALayer都有關(guān)于操作層次的方法,那么就會構(gòu)建出兩個不一樣層次的UIView樹和CALayer樹出來,這不是多余的嗎?
之前說了UIView和CALayer有各自不一樣的職責(zé),那么他們的樹也同樣的有各自不一樣的職責(zé),CALayer之前說得比較清楚了,他的職責(zé)是存儲數(shù)據(jù),那么所以CALayer樹的職責(zé)就是存儲數(shù)據(jù)的樹,以供你修改,做動畫和渲染等使用。UIView樹的話,先看UIView繼承了什么,UIView繼承了UIResponder,UIResponder是負責(zé)一些觸摸事件的類,所以UIView樹主要的職責(zé)就是做觸摸的傳遞,這個工作可以分為2部分,1部分是尋找你點擊到的那個view,另外一部分是把你的觸摸方法傳遞下去。在這里的話UIView樹其實是充當(dāng)了責(zé)任鏈職責(zé)。
什么是責(zé)任鏈?可以閱讀責(zé)任鏈模式