[iOS Animation]-CALayer 視覺效果


http://www.cnblogs.com/daxiaxiaohao/p/4272550.html

http://my.oschina.net/u/2438875/blog/512271


視覺效果

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

我們現在能做到那樣了么?

史蒂芬·喬布斯

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

圓角

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

CALayer有一個叫做conrnerRadius的屬性控制著圖層角的曲率。它是一個浮點數,默認為0(為0的時候就是直角),但是你可以把它設置成任意值。默認情況下,這個曲率值只影響背景顏色而不影響背景圖片或是子圖層。不過,如果把masksToBounds設置成YES的話,圖層里面的所有東西都會被截取。

我們可以通過一個簡單的項目來演示這個效果。在Interface Builder中,我們放置一些視圖,他們有一些子視圖。而且這些子視圖有一些超出了邊界(如圖4.1)。你可能無法看到他們超出了邊界,因為在編輯界面的時候,超出的部分總是被Interface Builder裁切掉了。不過,你相信我就好了 :)

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

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

清單4.1 設置cornerRadius和masksToBounds

@interface?ViewController ()

@property?(nonatomic, weak)?IBOutlet?UIView *layerView1;

@property?(nonatomic, weak)?IBOutlet?UIView *layerView2;

@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;

//enable clipping on the second layer

self.layerView2.layer.masksToBounds =?YES;

}

@end

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

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

單獨控制每個層的圓角曲率也不是不可能的。如果想創建有些圓角有些直角的圖層或視圖時,你可能需要一些不同的方法。比如使用一個圖層蒙板(本章稍后會講到)或者是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 加上邊框

@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.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 在iOS(左)和Mac OS(右)上shadowOffset的表現。

蘋果更傾向于用戶界面的陰影應該是垂直向下的,所以在iOS把陰影寬度設為0,然后高度設為一個正值不失為一個做法。

shadowRadius屬性控制著陰影的模糊度,當它的值是0的時候,陰影就和視圖一樣有一個非常確定的邊界線。當值越來越大的時候,邊界線看上去就會越來越模糊和自然。蘋果自家的應用設計更偏向于自然的陰影,所以一個非零值再合適不過了。

通常來講,如果你想讓視圖或控件非常醒目獨立于背景之外(比如彈出框遮罩層),你就應該給shadowRadius設置一個稍大的值。陰影越模糊,圖層的深度看上去就會更明顯(如圖4.6).

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

陰影裁剪

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

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

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

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

從技術角度來說,這個結果是可以是可以理解的,但確實又不是我們想要的效果。如果你想沿著內容裁切,你需要用到兩個圖層:一個只畫陰影的空的外圖層,和一個用masksToBounds裁剪內容的內圖層。

如果我們把之前項目的右邊用單獨的視圖把裁剪的視圖包起來,我們就可以解決這個問題(如圖4.9).

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

我們只把陰影用在最外層的視圖上,內層視圖進行裁剪。清單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 右邊視圖,不受裁切陰影的陰影視圖。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容