UIview
UIView是iOS系統中界面元素的基礎搞莺,所有的界面元素都是繼承自它主穗。它本身完全是由Core Animation來實現的温算。然而它真正的繪圖部分扣草,是由一個CALayer類來管理胆绊。每一個UIView都存在一個主CALayer層氨鹏,而UIView本身更像是一個CALayer的管理器。當視圖和圖層一起的時候,視圖為圖層提供了底層的事件處理,而圖層為視圖 提供了顯示的內容压状。
在 iOS 中也有一些單獨的 layer仆抵,比如 AVCaptureVideoPreviewLayer 和 CAShapeLayer跟继,它們不需要附加到 view 上就可以在屏幕上顯示內容。
UIView有個重要屬性layer镣丑,可以返回它的主CALayer實例舔糖;layerClass方法,可以返回主layer所使用的類莺匠。UIView也可以使用不同的CALayer來顯示金吗,添加OpenGL ES實現View繪制的使用:
CALayer *layer = myView.layer
+ (Class)layerClass; // default is [CALayer class].
Used when creating the underlying layer for the view.
+ (Class)layerClass
{
return ([CAEAGLLayer class]);
}
當我們使用 Cocoa 的視圖的時候,必須繼承 NSView 或者 UIView 并且重載 函數 drawRect:來顯示任何內容。但是 CALayer 實例可以直接使用,而無需繼承子類趣竣。 因為 CALayer 是一個鍵-值編碼兼容的容器類,你可以在實例里面存儲任意值,所以子類實例化完全可以避免摇庙。
CALayer
而每個可見的圖層樹由兩個相應的樹組成:一個是呈現樹,一個是渲染樹。
圖層樹表示每一層的對象模型值遥缕,即設定圖層的屬性值跟匆。
呈現樹表示當前動畫發(fā)生的時候,將要顯示的值通砍,例如需要修改圖層的背景顏色玛臂,則圖層樹中對應屬性的值會立即改變,而呈現樹則是在將要顯示給用戶的時候才進行更改封孙。
渲染樹是在渲染圖層的時候使用呈現樹的值迹冤。
使用特點
一般情況下,UIview管理著所有顯示視圖虎忌,圖層樹也管理著所有的CALayer泡徙。改變一些基本的屬性情況也是類似,比如:frame膜蠢,bounds等堪藐,其中有屬性的出入(position,anchorpoint挑围。礁竞。。)
前車之鑒杉辙,每一個UIView都是它的layer的delegate模捂,所以如果自己添加了一個sublayer,一般不要把sublayer的delegate設置為UIView,這樣繪圖的時候可能會出現不可預知的問題(我沒發(fā)現)蜘矢。所以狂男,最好是自己自定義自己的CALayer和delegate:
@interface MyLayer : CALayer
@end
@implementation MyLayer
-(void)drawInContext:(CGContextRef)ctx
{
//這個通常發(fā)生在你的圖層需要定制行為而委托又無法滿足需求的時候
}
@end
@interface MylayerDelegate: NSObject
@end
@implementation MylayerDelegate
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
{
UIGraphicsPushContext(ctx);
UIBezierPath* path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)]; //
[[UIColor blueColor] setFill];
[path fill];
UIGraphicsPopContext();
}
//重寫這個方法可處理動畫顯示問題 --->引出動畫
- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
return [NSNull null];
}
@end
在UIView中繪制圖形,獲取的上下文就是這個view對應的layer的上下文品腹。在渲染的時候岖食,就是把圖形渲染到對應的layer上。在執(zhí)行渲染操作的時候舞吭,本質上它的內部相當于執(zhí)行了 [self.layer drawInContext:ctx];
CALayer 顯示默認的提供內容的方式泡垃,DrawInContextN錾骸(1)我們可以繼承 CALayer直接重寫 重繪的顯示方法,一般不這么做兔毙;(2)而 DrawLayer 則是delegate委托中的方法,也可以提供內容兄春。但是得通過發(fā)送以下任何一個方法 setNeedsDisplay 或者 setNeedsDisplayInRect的消息 , 或者把圖層的 needsDisplayOnBoundsChange 屬性值設置為 YES澎剥。(3)使用包含圖片內容的 CGImageRef 來顯式的設置圖層的 contents 的屬性。
引出動畫
基本上你改變一個單獨的 layer 的任何屬性的時候赶舆,都會觸發(fā)一個從舊的值過渡到新值的簡單動畫(這就是所謂的可動畫狀態(tài) animatable)哑姚。然而,如果你改變的是 view 中 layer 的同一個屬性芜茵,它只會從這一幀直接跳變到下一幀叙量。盡管兩種情況中都有 layer,但是當 layer 附加在 view 上時九串,它的默認的隱式動畫的 layer 行為就不起作用了绞佩。
為什么不起作用?猪钮?品山?
無論何時一個動畫的layer屬性改變,layer都會尋找并進行何時的“action”來實行這個改變烤低,layer 將像文檔中所寫的的那樣去尋找動作肘交,整個過程分為五個步驟:
第一步中的在 view 和 layer 中交互的部分是最有意思的:
layer 通過向它的 delegate 發(fā)送 actionForLayer:forKey: 消息來詢問提供一個對應屬性變化的 action。delegate 可以通過返回以下三者之一來進行響應:
- 它可以返回一個動作對象扑馁,這種情況下 layer 將使用這個動作涯呻。
- 它可以返回一個 nil, 這樣 layer 就會到其他地方繼續(xù)尋找腻要。
- 它可以返回一個 NSNull 對象复罐,告訴 layer 這里不需要執(zhí)行一個動作,搜索也會就此停止雄家。
對于 view 中的 layer 來說市栗,對動作的搜索只會到第一步為止。
因此UIView 默認情況下禁止了 layer 動畫咳短,但是在 animation block 中又重新啟用了它們.這便是當一個屬性在動畫 block 之外被改變時沒有動畫填帽,但是當屬性在動畫 block 內被改變時,就帶上了動畫的原因咙好。