原文出自AsyncDisplayKit
(如今叫Texture
)文檔中的一篇關於圓角的文章:Corner Roundinghtml
當談到圓角處理,許多開發人員都堅持使用CALayer
的.cornerRadius
屬性。不幸的是,這個使用方便的屬性極大地增長了性能壓力,你應當在沒有其餘選擇時才使用這個屬性纔對。這篇文章將涵蓋:架構
CALayer
的.cornerRadius
Texture
中設置圓角的樣例.cornerRadius
的代價很大爲何.cornerRadius
的代價很大?由於使用CALayer
的.cornerRadius
屬性會在滾動期間爲60FPS的屏幕上觸發離屏渲染(offscreen rendering),即便該區域的內容沒有任何改變。這意味着GPU必須在每一幀上切換上下文(context),包括合成整個幀和每次使用.cornerRadius
所致使的附加遍歷之間(?)。app
重要的是,這些消耗不會顯示在Time Profiler中,由於它們會影響到CoreAnimation Render Server幫助App作的工做(?)。這種莽的不行的行爲消耗了許多設備的性能。在iPhone 四、4S和5 / 5C(以及相似的iPad / iPod)上,你能性能顯着降低。在更新版本的iPhone上,即便你看不到直接的影響,它也會使內存空間減小,從而更容易產生掉幀的狀況。性能
選擇圓角設置策時只須要考慮三件事:優化
譯者注:這裏的節點指的是
AsyncDisplayKit
(Texture
)中的最基本單位,至關於UIKit
中的UIView
。動畫
在圓角下方的移動指的是一切在圓角圖層下方的移動。例如,當一個有圓角的collection view cell在背景圖層上滾動時,背景將在圓角底下移動並移出圓角。spa
至於圓角處的移動,請想象一個較小的帶圓角的scroll view中包含了一張很大的圖片。在scroll view內部縮放和平移圖片時,圖片將在scroll view的各個圓角處移動。設計
上圖將圓角下方的移動高亮爲藍色而且將圓角處的移動高亮爲橙色。code
提示:在圓角對象內部能夠有無需通過圓角的移動。下圖展現了一塊綠色高亮的區域,與scroll view邊框有一個等同於圓角角度的內邊距,當這塊區域滾動時,就不算是在圓角處移動。cdn
根據上述的說法來調整你的設計,消除其中的一種圓角移動,能讓你在使用快速圓角技術時和使用.cornerRadius
時產生巨大區別(?)。
最後要考慮的是肯定全部四個角是否都在同一節點,或者是否有任何子節點與圓角區域相交。
預合成的圓角是指使用貝塞爾曲線路徑在CGContext
/UIGraphicsContext
中剪切內容(path.clip
)所繪製的圓角。 在這種狀況下,拐角將成爲圖像自己的一部分,並被整合到單個CALayer
中。 有兩種類型的預合成圓角。
最佳的方法是使用預合成的不透明角。這是可用的最有效方法,能夠作到無Alpha混合(儘管這比起離屏渲染沒那麼重要),那不幸的是,這種方法最不靈活。若是圓角圖像須要在某個背景上移動,則這個背景將須要爲純色才行。有一個小技巧是,你可使用帶紋理背景或照片背景來製做預合成圓角的,但一般來講你最好使用預合成的帶Alpha圓角。
第二種方法是涉及有預合成的帶Alpha圓角的貝塞爾曲線路徑,此方法很是靈活,應該是最經常使用的方法之一。這個方法確實會以整個內容的大小,產生Alpha混合的消耗,而且由於Alpha通道,會比不透明的預合成增長多25%的內存消耗。但這些消耗對於現代設備來講已經很小了,而且這和啓動.cornerRadius
致使離屏渲染所產生的消耗來講根本不是一個數量級的。
預合成圓角的一個關鍵限制是,圓角只能接觸一個節點,而不能與任何子節點相交。若是存在以上任何一種狀況,則必須使用clip corner。
請注意,在Texture
中節點對.cornerRadius
有特殊的優化,只有當你使用了.shouldRasterizeDescendants
後會自動實現預合成角。在啓用柵格化以前,請務必仔細考慮,所以,在未徹底瞭解該概念以前,請勿使用此選項。
若是你想要一個簡單的,純色的圓角矩形或圓形,
Texture
爲你提供了一些便利方法。請參閱UIImage + ASConveniences.h
,以瞭解使用預合成的角(支持Alpha和不透明)建立純色、圓角可調整大小的圖像的方法。這些很是適合用做ASButtonNode的背景或是圖片節點的佔位符。
該方法是將4個獨立的不透明角放置在須要圓角的區域上。該方法靈活,且有很好的性能。4個獨立的layer消耗較小的CPU功率,一個layer對應一個圓角。
切割角主要運用於兩種圓角狀況:
.cornerRadius
嗎?在不多數狀況下,是適合使用.cornerRadius
的,其中包括一個狀況是一個圓角內和圓角下都須要移動的動態區域。對於某些動畫,這是不可避免的。可是,在許多狀況下,很容易經過調整設計來消除這樣兩種移動中的一種。在圓角移動一節中討論了一種這樣的狀況。
當你屏幕上的內容不怎麼移動時,使用.cornerRadius
或是把它做爲一個簡易實現方式也沒有那麼糟糕。可是,當屏幕上出現了移動,即便這個移動的區域不包含圓角,也會致使額外的性能負擔。例如,在導航欄中具備一個圓形元素,並在其下方有一個scroll view,即便它們不重疊,也會有影響。屏幕上的全部內容會進行動畫處理,即便用戶不進行交互。另外,任何形式的屏幕刷新都會消耗關於圓角切割的性能。
有人建議使用CALayer
的.shouldRasterize
能夠提升.cornerRadius
屬性的性能。這是一個沒有很好理解清楚的選擇,是很危險的。當沒有東西致使圖層從新柵格化(沒有移動,沒有點擊更改顏色,不在會移動的tableView上等等),就可使用。一般,咱們不鼓勵這樣作,由於這很容易致使性能更加降低。對於不具有出色應用程序架構並堅持使用CALayer
的.cornerRadius
(例如,他們的應用程序性能不佳)的用戶,這可能能夠帶來有意義的變化。可是,若是您是從頭開始構建你的app的話,咱們強烈建議您選擇上述更好的圓角策略之一。
CALayer
的.shouldRasterize
與Texture
中節點的.shouldRasterizeDescendents
無關。啓用後,.shouldRasterizeDescendents
將阻止子節點的實際視圖和圖層的建立。
使用此流程圖選擇性能最佳的策略來解決圓角問題。
Texture
的支持如下代碼舉例說明了如何在Texture
中使用圓角的不一樣方法:
使用.cornerRadius
var cornerRadius: CGFloat = 20.0
photoImageNode.cornerRoundingType = ASCornerRoundingTypeDefaultSlowCALayer
photoImageNode.cornerRadius = cornerRadius
複製代碼
使用預合成圓角
var cornerRadius: CGFloat = 20.0
// Use precomposition for rounding corners
photoImageNode.cornerRoundingType = ASCornerRoundingTypePrecomposited
photoImageNode.cornerRadius = cornerRadius
複製代碼
使用切割角
var cornerRadius: CGFloat = 20.0
photoImageNode.cornerRoundingType = ASCornerRoundingTypeClipping
photoImageNode.backgroundColor = UIColor.white
photoImageNode.cornerRadius = cornerRadius
複製代碼
使用willDisplayNodeContentWithRenderingContext
來設置某區域圓角的切割路徑
var cornerRadius: CGFloat = 20.0
// Use the screen scale for corner radius to respect content scale
var screenScale: CGFloat = UIScreen.main.scale
photoImageNode.willDisplayNodeContentWithRenderingContext = { context, drawParameters in
var bounds: CGRect = context.boundingBoxOfClipPath()
var radius: CGFloat = cornerRadius * screenScale
var overlay = UIImage.as_resizableRoundedImage(withCornerRadius: radius, cornerColor: UIColor.clear, fill: UIColor.clear)
overlay.draw(in: bounds)
UIBezierPath(roundedRect: bounds, cornerRadius: radius).addClip()
}
複製代碼
使用ASImageNode
來給圖片加圓角和邊框。這是一個給頭像添加圓角的好例子。
var cornerRadius: CGFloat = 20.0
photoImageNode.imageModificationBlock = ASImageNodeRoundBorderModificationBlock(5.0, UIColor.orange)
複製代碼