[iOS Animation]-CALayer 視覺效果

視覺效果

嗯,圓和橢圓還不錯,但若是是帶圓角的矩形呢? git

咱們如今能作到那樣了麼? github

史蒂芬·喬布斯 框架

咱們在第三章『圖層幾何學』中討論了圖層的frame,第二章『寄宿圖』則討論了圖層的寄宿圖。可是圖層不只僅能夠是圖片或是顏色的容器;還有一系列內建的特性使得創造美麗優雅的使人深入的界面元素成爲可能。在這一章,咱們將會探索一些可以經過使用CALayer屬性實現的視覺效果。 動畫

圓角

圓角矩形是iOS的一個標誌性審美特性。這在iOS的每個地方都獲得了體現,不管是主屏幕圖標,仍是警告彈框,甚至是文本框。按照這流行程度,你可能會認爲必定有不借助Photoshop就能輕易建立圓角舉行的方法。恭喜你,猜對了。 ui

CALayer有一個叫作conrnerRadius的屬性控制着圖層角的曲率。它是一個浮點數,默認爲0(爲0的時候就是直角),可是你能夠把它設置成任意值。默認狀況下,這個曲率值隻影響背景顏色而不影響背景圖片或是子圖層。不過,若是把masksToBounds設置成YES的話,圖層裏面的全部東西都會被截取。 atom

咱們能夠經過一個簡單的項目來演示這個效果。在Interface Builder中,咱們放置一些視圖,他們有一些子視圖。並且這些子視圖有一些超出了邊界(如圖4.1)。你可能沒法看到他們超出了邊界,由於在編輯界面的時候,超出的部分老是被Interface Builder裁切掉了。不過,你相信我就行了 :) spa

圖4.1

圖4.1 兩個白色的大視圖,他們都包含了小一些的紅色視圖。 .net

而後在代碼中,咱們設置角的半徑爲20個點,並裁剪掉第一個視圖的超出部分(見清單4.1)。技術上來講,這些屬性均可以在Interface Builder的探測板中分別經過『用戶定義運行時屬性』和勾選『裁剪子視圖』(Clip Subviews)選擇框來直接設置屬性的值。不過,在這個示例中,代碼可以表示得更清楚。圖4.2是運行代碼的結果 設計

清單4.1 設置cornerRadius和masksToBounds 對象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface ViewController ()
 
@property (nonatomic, weak) IBOutlet UIView *layerView1;
@property (nonatomic, weak) IBOutlet UIView *layerView2;
 
 
@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;
 
  //enable clipping on the second layer
  self.layerView2.layer.masksToBounds = YES;
}
@end

圖4.2

右圖中,紅色的子視圖沿角半徑被裁剪了

如你所見,右邊的子視圖沿邊界被裁剪了。

單獨控制每一個層的圓角曲率也不是不可能的。若是想建立有些圓角有些直角的圖層或視圖時,你可能須要一些不一樣的方法。好比使用一個圖層蒙板(本章稍後會講到)或者是CAShapeLayer(見第六章『專用圖層』)。

圖層邊框

CALayer另外兩個很是有用屬性就是borderWidth和borderColor。兩者共同定義了圖層邊的繪製樣式。這條線(也被稱做stroke)沿着圖層的bounds繪製,同時也包含圖層的角。

borderWidth是以點爲單位的定義邊框粗細的浮點數,默認爲0.borderColor定義了邊框的顏色,默認爲黑色。

borderColor是CGColorRef類型,而不是UIColor,因此它不是Cocoa的內置對象。不過呢,你確定也清楚圖層引用了borderColor,雖然屬性聲明並不能證實這一點。CGColorRef在引用/釋放時候的行爲表現得與NSObject極其類似。可是Objective-C語法並不支持這一作法,因此CGColorRef屬性即使是強引用也只能經過assign關鍵字來聲明。

邊框是繪製在圖層邊界裏面的,並且在全部子內容以前,也在子圖層以前。若是咱們在以前的示例中(清單4.2)加入圖層的邊框,你就能看到究竟是怎麼一回事了(如圖4.3).

清單4.2 加上邊框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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;
 
  //enable clipping on the second layer
  self.layerView2.layer.masksToBounds = YES;
}
 
@end


圖4.3

圖4.3 給圖層增長一個邊框

仔細觀察會發現邊框並不會把寄宿圖或子圖層的形狀計算進來,若是圖層的子圖層超過了邊界,或者是寄宿圖在透明區域有一個透明蒙板,邊框仍然會沿着圖層的邊界繪製出來(如圖4.4).

圖4.4

圖4.4 邊框是跟隨圖層的邊界變化的,而不是圖層裏面的內容

陰影

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

圖4.5 在iOS(左)和Mac OS(右)上shadowOffset的表現。

蘋果更傾向於用戶界面的陰影應該是垂直向下的,因此在iOS把陰影寬度設爲0,而後高度設爲一個正值不失爲一個作法。

shadowRadius屬性控制着陰影的模糊度,當它的值是0的時候,陰影就和視圖同樣有一個很是肯定的邊界線。當值愈來愈大的時候,邊界線看上去就會愈來愈模糊和天然。蘋果自家的應用設計更偏向於天然的陰影,因此一個非零值再合適不過了。

一般來說,若是你想讓視圖或控件很是醒目獨立於背景以外(好比彈出框遮罩層),你就應該給shadowRadius設置一個稍大的值。陰影越模糊,圖層的深度看上去就會更明顯(如圖4.6).

圖4.6

圖4.6 大一些的陰影位移和角半徑會增長圖層的深度即視感

陰影裁剪

和圖層邊框不一樣,圖層的陰影繼承自內容的外形,而不是根據邊界和角半徑來肯定。爲了計算出陰影的形狀,Core Animation會將寄宿圖(包括子視圖,若是有的話)考慮在內,而後經過這些來完美搭配圖層形狀從而建立一個陰影(見圖4.7)。

圖4.7

圖4.7 陰影是根據寄宿圖的輪廓來肯定的

當陰影和裁剪扯上關係的時候就有一個頭疼的限制:陰影一般就是在Layer的邊界以外,若是你開啓了masksToBounds屬性,全部從圖層中突出來的內容都會被才剪掉。若是咱們在咱們以前的邊框示例項目中增長圖層的陰影屬性時,你就會發現問題所在(見圖4.8).

圖4.8

圖4.8 maskToBounds屬性裁剪掉了陰影和內容

從技術角度來講,這個結果是能夠是能夠理解的,但確實又不是咱們想要的效果。若是你想沿着內容裁切,你須要用到兩個圖層:一個只畫陰影的空的外圖層,和一個用masksToBounds裁剪內容的內圖層。

若是咱們把以前項目的右邊用單獨的視圖把裁剪的視圖包起來,咱們就能夠解決這個問題(如圖4.9).

圖4.9

圖4.9 右邊,用額外的陰影轉換視圖包裹被裁剪的視圖

咱們只把陰影用在最外層的視圖上,內層視圖進行裁剪。清單4.3是代碼實現,圖4.10是運行結果。

清單4.3 用一個額外的視圖來解決陰影裁切的問題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@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

圖4.10 右邊視圖,不受裁切陰影的陰影視圖。

相關文章
相關標籤/搜索