看山不是山,再談iOS圓角優(yōu)化

設(shè)計趨勢

隨著移動互聯(lián)網(wǎng)的發(fā)展,在UI設(shè)計領(lǐng)域,圓角和陰影逐漸成為一種不可或缺的設(shè)計手法,這離不開系統(tǒng)廠商對于設(shè)計風(fēng)格的引導(dǎo),比如:Apple產(chǎn)品的設(shè)計風(fēng)格和UI設(shè)計規(guī)范、Google Material Design等等。不可否認,圓角設(shè)計確實會增強產(chǎn)品的親和力和優(yōu)雅感,但是作為開發(fā)者,我們卻不那么喜歡它,因為通常大量圓角+陰影會顯著降低界面滑動的流暢度。

image.png

是什么造成了卡頓

有沒有圓角和陰影對于CPU來說沒有太大差別,額外的效果會給GPU帶來較大的壓力。通過Xcode提供的,View debugging可以看到,有沒有圓角在渲染上的差別在于離屏渲染。離屏渲染就是圖層無法直接在當前屏幕繪制,而要開辟新的緩沖區(qū)進行繪制,然后在切換到當前緩沖區(qū)進行展示,中間就包含了不少性能消耗。

離屏渲染發(fā)生與否取決于技術(shù)實現(xiàn),iOS在發(fā)展過程中,也在盡量的減少離屏渲染發(fā)的發(fā)生。例如:在iOS9上設(shè)置了圓角并且maskToBounds就會觸發(fā)離屏渲染,而在iOS11以后則需要在子視圖也設(shè)置了圓角時才會觸發(fā)。所以最好的辦法就是通過Xcode提供的View debugging來查看。

優(yōu)化之預(yù)合成圓角

在圓角優(yōu)化方面,目前主流的技術(shù)方案是,圖片下載下來后根據(jù)圓角大小來重新繪制一張切過圓角的PNG圖片,然后將此圖片展示到UI控件上,這樣控件就不必再設(shè)置圓角。

image.png

預(yù)合成圓角避免了離屏渲染,性能表現(xiàn)很不錯,但是依然存在幾點問題:

  1. 如果控件是Gif或者視頻,預(yù)先渲染將無法實施
  2. 每張圖片都需要重新繪制,會帶來額外的CPU開銷
  3. 原始圖片和重繪后的圖片都需要存入緩存,帶來較大的內(nèi)存占用
  4. 同樣一張圖片,如果展示的尺寸和圓角值不一樣,則需要重繪多個版本
  5. 通常圖標上會加入子視圖,無法被天然裁切
image.png

換個思路,探索適用于90%場景的障眼之法

雖然我們看到的是圓角,但可能實際不是個圓角。相信有人用過這種方式生成圓角效果,簡單高效,通過圖層疊加,讓圖片看起來像是有圓角。這種方案的問題在于:不同尺寸、圓角大小、描邊及陰影效果都需要提供單獨的圖片,通用性非常不好。

image.png

拋開問題不說,這條技術(shù)路線有沒有價值呢?為此統(tǒng)計了大多數(shù)app中的圓角設(shè)計,發(fā)現(xiàn)絕大多數(shù)圓角陰影效果都可以通過這種方式實現(xiàn)。

image.png

不能實現(xiàn)的是背景非純色的場景

image.png

所以總體看來基于圖層疊加的圓角優(yōu)化技術(shù)有其獨特價值。

圓角與陰影的實現(xiàn)

利用CoreGraphics可以繪制圖片的圓角、陰影和描邊,具體的不再展開。

image.png

image.png

尺寸的自適應(yīng)

關(guān)鍵問題來了,這個蒙層要繪制多大?

通常一套頁面設(shè)計中,控件都使用一致的圓角和陰影效果,但是控件尺寸會有不同。如果每個尺寸的控件都要繪制一套蒙層,雖然技術(shù)上可以實現(xiàn),但是不免讓人遺憾。

經(jīng)過反復(fù)求證,發(fā)現(xiàn)求得一個最小尺寸,來滿足更大尺寸的自適應(yīng)是可行的,這就解決了一張圖適用多個尺寸的問題。

image.png

受限于cornerRadius、offset、blur的大小,自適應(yīng)蒙層有一個自適應(yīng)最小值,當控件尺寸小于蒙層尺寸時則需通過另一套策略繪制,在工程實踐中還沒有遇到這種場景,這一塊暫時沒有支持。

CALayer中關(guān)于自適應(yīng)的設(shè)置,contentCenter的范圍是0~1.0

@property CGRect contentsCenter

SKCornerLayer支持的效果

圓角 陰影 描邊 異步 緩存 DarkMode

使用方法

// 設(shè)置陰影的顏色、blur、offset、圓角支持dark mode
self.shadowView.layer.skCorner
        .sShadowColor(colorDynamicFromRGBA(0x535459, 0.15, 0x0, 0))
        .sShadowBlur(15)
        .sShadowOffset(CGSizeMake(0, 2))
        .sCornerRadiusAll(8)
        .sCornerBackgroundColor(colorDynamicFromRGB(0xffffff, 0x1e1e1e));

// 異步繪制
self.shadowView.layer.skCornerLayer.displayCornerAsynchronously = YES;

性能量化

這里構(gòu)了一個場景,左邊使用SKCornerLayer,右邊使用默認的圓角+陰影(采用了默認最優(yōu)配置:圓角view沒有嵌套,陰影指定shadowPath,沒有產(chǎn)生離屏渲染)。測試中采用,通過timer以相同的速度滾動列表,以產(chǎn)生穩(wěn)定的GPU占用率。

image.png
image.png

在iOS9上即使圓角后的view沒有嵌套,也會產(chǎn)生離屏渲染,GPU開銷會更大

測試設(shè)備iPhone 6sPlus iOS12.2

這里設(shè)置了兩個對比組,blur分別為15和5,分別可以提升GPU性能約40%和28%,總體來看blur值越大優(yōu)化效果越好。

image.png

關(guān)于圓角和陰影的一些建議

  1. 父視圖有圓角時避免子視圖包含圓角效果,解決辦法是,視圖層級扁平化
  2. 對性能要求不高的場景下,通過指定shadowPath來生成陰影也是個不錯的選擇
  3. 對性能有較高要求的場景下,建議使用SKCornerLayer或者切圖來構(gòu)建陰影
  4. 最后使用View debugging檢查視圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容