## 陰影
????iOS的另一個常見特性呢,就是陰影。陰影往往可以達到圖層深度暗示的效果。也能夠用來強調正在顯示的圖層和優先級(比如說一個在其他視圖之前的彈出框),不過有時候他們只是單純的裝飾目的。
????給`shadowOpacity`屬性一個大于默認值(也就是0)的值,陰影就可以顯示在任意圖層之下。`shadowOpacity`是一個必須在0.0(不可見)和1.0(完全不透明)之間的浮點數。如果設置為1.0,將會顯示一個有輕微模糊的黑色陰影稍微在圖層之上。若要改動陰影的表現,你可以使用CALayer的另外三個屬性:`shadowColor`,`shadowOffset`和`shadowRadius`。
????顯而易見,`shadowColor`屬性控制著陰影的顏色,和`borderColor`和`backgroundColor`一樣,它的類型也是`CGColorRef`。陰影默認是黑色,大多數時候你需要的陰影也是黑色的(其他顏色的陰影看起來是不是有一點點奇怪。。)。
????`shadowOffset`屬性控制著陰影的方向和距離。它是一個`CGSize`的值,寬度控制這陰影橫向的位移,高度控制著縱向的位移。`shadowOffset`的默認值是 {0, -3},意即陰影相對于Y軸有3個點的向上位移。
????為什么要默認向上的陰影呢?盡管Core Animation是從圖層套裝演變而來(可以認為是為iOS創建的私有動畫框架),但是呢,它卻是在Mac OS上面世的,前面有提到,二者的Y軸是顛倒的。這就導致了默認的3個點位移的陰影是向上的。在Mac上,`shadowOffset`的默認值是陰影向下的,這樣你就能理解為什么iOS上的陰影方向是向上的了(如圖4.5).

圖4.5 在iOS(左)和Mac OS(右)上`shadowOffset`的表現。
????蘋果更傾向于用戶界面的陰影應該是垂直向下的,所以在iOS把陰影寬度設為0,然后高度設為一個正值不失為一個做法。
????`shadowRadius`屬性控制著陰影的*模糊度*,當它的值是0的時候,陰影就和視圖一樣有一個非常確定的邊界線。當值越來越大的時候,邊界線看上去就會越來越模糊和自然。蘋果自家的應用設計更偏向于自然的陰影,所以一個非零值再合適不過了。
????通常來講,如果你想讓視圖或控件非常醒目獨立于背景之外(比如彈出框遮罩層),你就應該給`shadowRadius`設置一個稍大的值。陰影越模糊,圖層的深度看上去就會更明顯(如圖4.6).

## 陰影裁剪
&nbps;???和圖層邊框不同,圖層的陰影繼承自內容的外形,而不是根據邊界和角半徑來確定。為了計算出陰影的形狀,Core Animation會將寄宿圖(包括子視圖,如果有的話)考慮在內,然后通過這些來完美搭配圖層形狀從而創建一個陰影(見圖4.7)。

圖4.7 陰影是根據寄宿圖的輪廓來確定的
&nbps;???當陰影和裁剪扯上關系的時候就有一個頭疼的限制:陰影通常就是在Layer的邊界之外,如果你開啟了`masksToBounds`屬性,所有從圖層中突出來的內容都會被才剪掉。如果我們在我們之前的邊框示例項目中增加圖層的陰影屬性時,你就會發現問題所在(見圖4.8).

圖4.8?`maskToBounds`屬性裁剪掉了陰影和內容
&nbps;???從技術角度來說,這個結果是可以是可以理解的,但確實又不是我們想要的效果。如果你想沿著內容裁切,你需要用到兩個圖層:一個只畫陰影的空的外圖層,和一個用`masksToBounds`裁剪內容的內圖層。
&nbps;???如果我們把之前項目的右邊用單獨的視圖把裁剪的視圖包起來,我們就可以解決這個問題(如圖4.9).

圖4.9 右邊,用額外的陰影轉換視圖包裹被裁剪的視圖
&nbps;???我們只把陰影用在最外層的視圖上,內層視圖進行裁剪。清單4.3是代碼實現,圖4.10是運行結果。
清單4.3 用一個額外的視圖來解決陰影裁切的問題
~~~
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView1;
@property (nonatomic, weak) IBOutlet UIView *layerView2;
@property (nonatomic, weak) IBOutlet UIView *shadowView;
@end
@implementation ViewController
?
- (void)viewDidLoad
{
[super viewDidLoad];
//set the corner radius on our layers
self.layerView1.layer.cornerRadius = 20.0f;
self.layerView2.layer.cornerRadius = 20.0f;
//add a border to our layers
self.layerView1.layer.borderWidth = 5.0f;
self.layerView2.layer.borderWidth = 5.0f;
//add a shadow to layerView1
self.layerView1.layer.shadowOpacity = 0.5f;
self.layerView1.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
self.layerView1.layer.shadowRadius = 5.0f;
//add same shadow to shadowView (not layerView2)
self.shadowView.layer.shadowOpacity = 0.5f;
self.shadowView.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
self.shadowView.layer.shadowRadius = 5.0f;
//enable clipping on the second layer
self.layerView2.layer.masksToBounds = YES;
}
@end
~~~

圖4.10 右邊視圖,不受裁切陰影的陰影視圖。
### shadowPath屬性
????我們已經知道圖層陰影并不總是方的,而是從圖層內容的形狀繼承而來。這看上去不錯,但是實時計算陰影也是一個非常消耗資源的,尤其是圖層有多個子圖層,每個圖層還有一個有透明效果的寄宿圖的時候。
????如果你事先知道你的陰影形狀會是什么樣子的,你可以通過指定一個`shadowPath`來提高性能。`shadowPath`是一個`CGPathRef`類型(一個指向`CGPath`的指針)。`CGPath`是一個Core Graphics對象,用來指定任意的一個矢量圖形。我們可以通過這個屬性單獨于圖層形狀之外指定陰影的形狀。
圖4.11 展示了同一寄宿圖的不同陰影設定。如你所見,我們使用的圖形很簡單,但是它的陰影可以是你想要的任何形狀。清單4.4是代碼實現。

圖4.11 用`shadowPath`指定任意陰影形狀
清單4.4 創建簡單的陰影形狀
~~~
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView1;
@property (nonatomic, weak) IBOutlet UIView *layerView2;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//enable layer shadows
self.layerView1.layer.shadowOpacity = 0.5f;
self.layerView2.layer.shadowOpacity = 0.5f;
//create a square shadow
CGMutablePathRef squarePath = CGPathCreateMutable();
CGPathAddRect(squarePath, NULL, self.layerView1.bounds);
self.layerView1.layer.shadowPath = squarePath; CGPathRelease(squarePath);
?//create a circular shadow
CGMutablePathRef circlePath = CGPathCreateMutable();
CGPathAddEllipseInRect(circlePath, NULL, self.layerView2.bounds);
self.layerView2.layer.shadowPath = circlePath; CGPathRelease(circlePath);
}
@end
~~~
????如果是一個矩形或者是圓,用`CGPath`會相當簡單明了。但是如果是更加復雜一點的圖形,`UIBezierPath`類會更合適,它是一個由UIKit提供的在CGPath基礎上的Objective-C包裝類。
圖4.6 大一些的陰影位移和角半徑會增加圖層的深度即視感
- 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 總結