引言
在前幾篇博客中我們討論了圖層的frame,bounds,position以及讓圖層加載圖片。但是圖層事實上不僅可以顯示圖片,或者規則的矩形塊,它還有一系列內建的特性來創建美麗優雅的頁面元素。在這篇博客中我們就來探索一下CALayer的視覺效果。
視覺效果
圖層的一些基礎視覺效果其實在我們的日常開發過程中也經常會用到,比如圓角,邊框,陰影,還有一些不常用的效果比如蒙版,下面我們就來一一討論一下。
圖層圓角
近些年圓角矩形幾乎成為了主流的審美特性,不管是圖標,還是頁面元素,甚至文本輸入框也都是按照圓角矩形來設計的。
CALayer有一個叫做cornerRadius的屬性來控制著圖層的圓角曲率,它是一個浮點型默認為0也就是直角。通過修改它為一個大于0的值,可以實現CALayer的圓角,默認情況下這個值只影響本圖層的背景顏色,而不影響圖層的背景圖片或者是子圖層,不過如果把maskesToBounds設置成為YES的話,圖層里面的所有東西都會被截取。
未設置masksToBounds屬性:
let whiteLayer = CALayer()whiteLayer.backgroundColor = UIColor.white.cgColorwhiteLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)whiteLayer.position = self.view.centerwhiteLayer.cornerRadius = 20.0self.view.layer.addSublayer(whiteLayer)let yellowLayer = CALayer()yellowLayer.backgroundColor = UIColor.yellow.cgColoryellowLayer.frame = CGRect(x: -50, y: -50, width: 100, height: 100)whiteLayer.addSublayer(yellowLayer)
效果如下:
增加masksToBounds屬性為true:
whiteLayer.masksToBounds = true
效果如下:
圖層邊框
CALayer的兩個常用屬性borderWidth和borderColor,兩個屬性共同決定了圖層邊框的樣式。邊框沿著圖層的bounds往內繪制,同時也包含圖層的圓角。
borderWidth屬性定義了邊框的寬度,是浮點數。
borderColor屬性定義了邊框的顏色默認是黑色,類型為CGColorRef。
我們使用上面的代碼為圖層添加邊框 - 未設置masksToBounds:
let whiteLayer = CALayer()whiteLayer.backgroundColor = UIColor.white.cgColorwhiteLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)whiteLayer.position = self.view.centerwhiteLayer.cornerRadius = 20.0whiteLayer.borderWidth = 2.0whiteLayer.borderColor = UIColor.blue.cgColorself.view.layer.addSublayer(whiteLayer)let yellowLayer = CALayer()yellowLayer.backgroundColor = UIColor.yellow.cgColoryellowLayer.frame = CGRect(x: -50, y: -50, width: 100, height: 100)whiteLayer.addSublayer(yellowLayer)
效果如下:
我們發現當設置邊框時并不會把寄宿圖或者是子圖層的形狀計算出來,而是沿著圖層的邊界進行繪制的。
圖層陰影
iOS中陰影也是一個十分常見的特性,關于圖層陰影的設置涉及到多個屬性共同作用。
shadowOpacity:修改這個屬性為一個大于0的值,陰影就可以顯示在任意圖層之下。它是一個介于0和1之間的浮點數。
shadowColor:控制陰影的顏色,它的類型也是CGColorRef,默認為黑色。
shadowOffset:控制陰影的方向和距離,它是一個CGSize值,寬度控制這個陰影的橫向位移,高度控制陰影的縱向位移。默認值為{0,-3}向上偏移3。
shadowRadius:控制陰影的模糊程度,當設置為0的時候陰影就和圖層一樣有一個非常確定的邊界線,當值越大邊界線看上去就會越模糊和自然。
我們來創建一個橙色的圖層并為它添加陰影效果,代碼如下:
self.view.backgroundColor = .whitelet originLayer = CALayer()originLayer.backgroundColor = UIColor.orange.cgColororiginLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)originLayer.position = self.view.centerself.view.layer.addSublayer(originLayer)originLayer.shadowOpacity = 0.7originLayer.shadowOffset = CGSize(width: 0, height: 3)originLayer.shadowRadius = 3originLayer.shadowColor = UIColor.black.cgColor
效果如下:
shadowPath:當我們給圖層設置陰影屬性的時候發現還有一個屬性我們沒有介紹到shadowPath。
這就意味著陰影的形狀我們可以隨意繪制,shadowPath是一個CGPathRef類型(一個指向CGPath的指針),CGPath是一個Core Graphics對象,用來指定任意的一個矢量圖形。
來修改一下上面的代碼為圖層添加一個圓形的陰影,代碼如下:
let originLayer = CALayer()originLayer.backgroundColor = UIColor.orange.cgColororiginLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)originLayer.position = self.view.centerself.view.layer.addSublayer(originLayer)originLayer.shadowOpacity = 0.7originLayer.shadowOffset = CGSize(width: 0, height: 3)originLayer.shadowRadius = 3originLayer.shadowColor = UIColor.black.cgColorlet circlePath = CGPath(roundedRect: CGRect(x: -25, y: -25, width: 250, height: 250), cornerWidth: 250, cornerHeight: 250, transform: nil)originLayer.shadowPath = circlePath
效果如下:
圖層的陰影另外還有兩個特殊的地方,它和圖層的邊框不同,陰影繼承自圖層內容的外形,而不是圖層的邊界和角半徑。為了計算出陰影的形狀,Core Animation會將寄宿圖考慮在內,包括子視圖。
下面我們來加載一個圖像,代碼如下:
let originLayer = CALayer()originLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)originLayer.position = self.view.centerself.view.layer.addSublayer(originLayer)originLayer.shadowOpacity = 0.7originLayer.shadowOffset = CGSize(width: 0, height: 3)originLayer.shadowRadius = 3originLayer.shadowColor = UIColor.black.cgColororiginLayer.contents = UIImage(named: "icon_dynamic_like_selected")?.cgImageoriginLayer.contentsScale = UIScreen.main.scale
效果如下,Core Animation為我們創建了一個心形的陰影因為圖層的內容為心形:
圖層陰影的另外一個特殊的地方在于當我們設置masksToBounds的屬性為true之后,超出圖層部分的陰影會被裁剪掉。
這樣給圓角矩形設置陰影的時候就需要花點小心思,比如使用兩個相同的圖層其中一個設置陰影。
圖層蒙版
還有的時候我們希望展示的內容不是在一個矩形也不是圓角矩形,比如說你想顯示一個星星,或者顯示一個鏤空的文字。這個時候我們可以使用圖層蒙版來是現實。
CALayer有一個mask屬性,這個屬性本身就是CALayer類型,它類似一個子圖層,它相對父圖層進行布局,但是它卻不是一個普通的子圖層,mask圖層定義了父圖層的部分可見區域。
mask圖層的color屬性是無關緊要的,真正重要的是圖層的輪廓。mask屬性就像一個模型切割機,mask圖層實心的部分也就是不透明的部分會被保留下來。
如果mask圖層比父圖層小,那么只有在mask圖層里面的內容才是它關心的,除此之外的一切都會被隱藏起來。
下面我們來創建一個例子:
???????
當我們設置為子圖層的時候顯示效果如下:
當我們設置為mask的時候代碼如下:
// 背景let bgLayer = CALayer()bgLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)bgLayer.position = self.view.centerbgLayer.contents = UIImage(named: "random_dynamic_pic_0")?.cgImagebgLayer.contentsScale = UIScreen.main.scaleself.view.layer.addSublayer(bgLayer)// 蒙版let maskLayer = CALayer()maskLayer.frame = CGRect(x: 70, y: 70, width: 50, height: 50)maskLayer.contents = UIImage(named: "icon_dynamic_like_selected")?.cgImagemaskLayer.contentsScale = UIScreen.main.scalebgLayer.mask = maskLayer
效果如下:
圖層的拉伸過濾方式
最后我們再來談一下圖層的minificationFilter和magnificationFilter屬性,這兩個屬性我們不常用,他們定義了圖片在被拉伸或者被壓縮時采用的拉伸過濾方式。
CALayer提供了三種拉伸過濾方式:
- CALayerContentsFilter.linear
- CALayerContentsFilter.nearest
- CALayerContentsFilter.trilinear
通常來講這兩個屬性的默認值都是.linear,既采用雙線性濾波算法過濾器進行壓縮和拉伸,大多數情況下都表現良好,雙線性濾波算法通過對個像素取樣來生成最終的值,會得到一個還不錯的拉伸效果,但是當放大倍數比較大的時候就模糊不清了。
.trailinear和.linear非常相似,大部分情況下二者都看不出來有啥區別。相對雙線性濾波算法,三線性濾波算法存儲了多個大小情況下的圖片,并三維取樣,同時結合大圖和小圖的存儲進而得到最后的結果。
.nearest是一種比較武斷的方案,這個算法就是取樣最近的單像素點,而不管其它的顏色,這樣做非常快。但是最明顯的效果就是會使得壓縮圖片更糟,放大之后也會有明顯的馬賽克,但是它也有適用的地方,比如對于沒有斜線的小圖來說最近過濾算法就要好很多。
下面舉兩個例子,一個帶斜線的小圖,圖片的原始大小是50*30,我們分別使用三種算法來進行放大3倍。
代碼如下:
// 原始比例let orginLayer = CALayer()orginLayer.frame = CGRect(x: 70, y: 100, width: 50, height: 30)orginLayer.contents = UIImage(named: "Property 1=ktv")?.cgImageorginLayer.contentsScale = UIScreen.main.scaleself.view.layer.addSublayer(orginLayer)// 放大3倍 - 雙線性濾波算法let magLayer = CALayer()magLayer.frame = CGRect(x: 70, y: 200, width: 50 * 3, height: 30 * 3)magLayer.contents = UIImage(named: "Property 1=ktv")?.cgImagemagLayer.contentsScale = UIScreen.main.scalemagLayer.magnificationFilter = .linearself.view.layer.addSublayer(magLayer)// 放大3倍 - 三線性濾波算法let magLayer1 = CALayer()magLayer1.frame = CGRect(x: 70, y: 300, width: 50 * 3, height: 30 * 3)magLayer1.contents = UIImage(named: "Property 1=ktv")?.cgImagemagLayer1.contentsScale = UIScreen.main.scalemagLayer1.magnificationFilter = .trilinearself.view.layer.addSublayer(magLayer1)// 放大3倍 - 最近鄰濾波算法let magLayer2 = CALayer()magLayer2.frame = CGRect(x: 70, y: 400, width: 50 * 3, height: 30 * 3)magLayer2.contents = UIImage(named: "Property 1=ktv")?.cgImagemagLayer2.contentsScale = UIScreen.main.scalemagLayer2.magnificationFilter = .nearestself.view.layer.addSublayer(magLayer2)
效果如下:
我們來更換一張不帶斜邊的圖片,效果如下:
總的來說呢,相對于比較小的圖或者是差異特別明顯,極少斜線的大圖,最近過濾算法會保留這種差異明顯的特性以呈現更好的結果。
但是對于大多數圖圖尤其是有很多斜線或者曲線的圖片來說,雙線性和三線形濾波算法的結果更好些,而最近過濾算法會極差。
換句話說,線性過濾保留了形狀,而最近過濾保留了像素差異。
總結
本篇博客介紹了一些使用代碼可以實現的圖層的視覺特效,比如陰影,蒙版,圓角。又介紹了一些拉伸過濾的方案。
下篇博客我們開始研究圖層的變化。