# Custom Drawing
????給`contents`賦CGImage的值不是唯一的設置寄宿圖的方法。我們也可以直接用Core Graphics直接繪制寄宿圖。能夠通過繼承UIView并實現`-drawRect:`方法來自定義繪制。
????`-drawRect:`?方法沒有默認的實現,因為對UIView來說,寄宿圖并不是必須的,它不在意那到底是單調的顏色還是有一個圖片的實例。如果UIView檢測到`-drawRect:`?方法被調用了,它就會為視圖分配一個寄宿圖,這個寄宿圖的像素尺寸等于視圖大小乘以?`contentsScale`的值。
????如果你不需要寄宿圖,那就不要創建這個方法了,這會造成CPU資源和內存的浪費,這也是為什么蘋果建議:如果沒有自定義繪制的任務就不要在子類中寫一個空的-drawRect:方法。
????當視圖在屏幕上出現的時候?`-drawRect:`方法就會被自動調用。`-drawRect:`方法里面的代碼利用Core Graphics去繪制一個寄宿圖,然后內容就會被緩存起來直到它需要被更新(通常是因為開發者調用了`-setNeedsDisplay`方法,盡管影響到表現效果的屬性值被更改時,一些視圖類型會被自動重繪,如`bounds`屬性)。雖然`-drawRect:`方法是一個UIView方法,事實上都是底層的CALayer安排了重繪工作和保存了因此產生的圖片。
????CALayer有一個可選的`delegate`屬性,實現了`CALayerDelegate`協議,當CALayer需要一個內容特定的信息時,就會從協議中請求。CALayerDelegate是一個非正式協議,其實就是說沒有CALayerDelegate @protocol可以讓你在類里面引用啦。你只需要調用你想調用的方法,CALayer會幫你做剩下的。(`delegate`屬性被聲明為id類型,所有的代理方法都是可選的)。
????當需要被重繪時,CALayer會請求它的代理給他一個寄宿圖來顯示。它通過調用下面這個方法做到的:
~~~
(void)displayLayer:(CALayerCALayer *)layer;
~~~
????趁著這個機會,如果代理想直接設置`contents`屬性的話,它就可以這么做,不然沒有別的方法可以調用了。如果代理不實現`-displayLayer:`方法,CALayer就會轉而嘗試調用下面這個方法:
~~~
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
~~~
????在調用這個方法之前,CALayer創建了一個合適尺寸的空寄宿圖(尺寸由`bounds`和`contentsScale`決定)和一個Core Graphics的繪制上下文環境,為繪制寄宿圖做準備,他作為ctx參數傳入。
????讓我們來繼續第一章的項目讓它實現CALayerDelegate并做一些繪圖工作吧(見清單2.5).圖2.12是他的結果
清單2.5 實現CALayerDelegate
~~~
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
?
//create sublayer
CALayer *blueLayer = [CALayer layer];
blueLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
blueLayer.backgroundColor = [UIColor blueColor].CGColor;
//set controller as layer delegate
blueLayer.delegate = self;
//ensure that layer backing image uses correct scale
blueLayer.contentsScale = [UIScreen mainScreen].scale; //add layer to our view
[self.layerView.layer addSublayer:blueLayer];
//force layer to redraw
[blueLayer display];
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
//draw a thick red circle
CGContextSetLineWidth(ctx, 10.0f);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextStrokeEllipseInRect(ctx, layer.bounds);
}
@end
~~~

圖2.12 實現CALayerDelegate來繪制圖層
注意一下一些有趣的事情:
* 我們在blueLayer上顯式地調用了`-display`。不同于UIView,當圖層顯示在屏幕上時,CALayer不會自動重繪它的內容。它把重繪的決定權交給了開發者。
* 盡管我們沒有用`masksToBounds`屬性,繪制的那個圓仍然沿邊界被裁剪了。這是因為當你使用CALayerDelegate繪制寄宿圖的時候,并沒有對超出邊界外的內容提供繪制支持。
????現在你理解了CALayerDelegate,并知道怎么使用它。但是除非你創建了一個單獨的圖層,你幾乎沒有機會用到CALayerDelegate協議。因為當UIView創建了它的宿主圖層時,它就會自動地把圖層的delegate設置為它自己,并提供了一個`-displayLayer:`的實現,那所有的問題就都沒了。
????當使用寄宿了視圖的圖層的時候,你也不必實現`-displayLayer:`和`-drawLayer:inContext:`方法來繪制你的寄宿圖。通常做法是實現UIView的`-drawRect:`方法,UIView就會幫你做完剩下的工作,包括在需要重繪的時候調用`-display`方法。
- Introduction
- 1. 圖層樹
- 1.1 圖層與視圖
- 1.2 圖層的能力
- 1.3 使用圖層
- 1.4 總結
- 2. 寄宿圖
- 2.1 contents屬性
- 2.2 Custom Drawing
- 2.3 總結
- 3. 圖層幾何學
- 3.1 布局
- 3.2 錨點
- 3.3 坐標系
- 3.4 Hit Testing
- 3.5 自動布局
- 3.6 總結
- 4. 視覺效果
- 4.1 圓角
- 4.2 圖層邊框
- 4.3 陰影
- 4.4 圖層蒙板
- 4.5 拉伸過濾
- 4.6 組透明
- 4.7 總結
- 5. 變換
- 5.1 仿射變換
- 5.2 3D變換
- 5.3 固體對象
- 5.4 總結
- 6. 專用圖層
- 6.1 CAShapeLayer
- 6.2 CATextLayer
- 6.3 CATransformLayer
- 6.4 CAGradientLayer
- 6.5 CAReplicatorLayer
- 6.6 CAScrollLayer
- 6.7 CATiledLayer
- 6.8 CAEmitterLayer
- 6.9 CAEAGLLayer
- 6.10 AVPlayerLayer
- 6.11 總結
- 7. 隱式動畫
- 7.1 事務
- 7.2 完成塊
- 7.3 圖層行為
- 7.4 呈現與模型
- 7.5 總結
- 8. 顯式動畫
- 8.1 屬性動畫
- 8.2 動畫組
- 8.3 過渡
- 8.4 在動畫過程中取消動畫
- 8.5 總結
- 9. 圖層時間
- 9.1 CAMediaTiming協議
- 9.2 層級關系時間
- 9.3 手動動畫
- 9.4 總結
- 10. 緩沖
- 10.1 動畫速度
- 10.2 自定義緩沖函數
- 10.3 總結
- 11. 基于定時器的動畫
- 11.1 定時幀
- 11.2 物理模擬
- 12. 性能調優
- 12.1. CPU VS GPU
- 12.2 測量,而不是猜測
- 12.3 Instruments
- 12.4 總結
- 13. 高效繪圖
- 13.1 軟件繪圖
- 13.2 矢量圖形
- 13.3 臟矩形
- 13.4 異步繪制
- 13.5 總結
- 14. 圖像IO
- 14.1 加載和潛伏
- 14.2 緩存
- 14.3 文件格式
- 14.4 總結
- 15. 圖層性能
- 15.1 隱式繪制
- 15.2 離屏渲染
- 15.3 混合和過度繪制
- 15.4 減少圖層數量
- 15.5 總結