離屏渲染
關(guān)于 UITableView、UICollectionView 滑動卡頓仍源,大家都會想起一個大殺手:圓角童本。
說起圓角,其實是針對 UITableView几颜、UICollectionView 等列表視圖 Cell 及其子視圖中包含的需要重復(fù)大量切圓角的場景倍试,關(guān)于這個問題當(dāng)時在開發(fā)圈里也曾經(jīng)引起過熱議,解決方案也挺多蛋哭,這里就不再過多的去討論县习。
不過有一點值得注意的是,可能是蘋果也意識到離屏渲染會產(chǎn)生性能問題谆趾,所以 iOS9 及之后的 iOS 版本對 UIImageView 離屏渲染做了一些優(yōu)化躁愿。
iOS 版本上的優(yōu)化
iOS 9.0 之前 UIImageView 跟 UIButton 設(shè)置圓角都會觸發(fā)離屏渲染。
iOS 9.0 之后 UIButton 設(shè)置圓角會觸發(fā)離屏渲染沪蓬,而 UIImageView 里 png 圖片設(shè)置圓角不會觸發(fā)離屏渲染了彤钟,但是其他類型的圖片依然會觸發(fā)離屏渲染,而且如果設(shè)置其他陰影效果之類的也是會觸發(fā)離屏渲染的跷叉。
Instruments 監(jiān)測離屏渲染
Instruments 的 Core Animation 工具中有幾個和離屏渲染相關(guān)的檢查選項:
- Color Offscreen-Rendered Yellow
開啟后會把那些需要離屏渲染的圖層高亮成黃色逸雹,這就意味著黃色圖層可能存在性能問題营搅。 - Color Hits Green and Misses Red
如果 shouldRasterize 被設(shè)置成 YES,對應(yīng)的渲染結(jié)果會被緩存梆砸,如果圖層是綠色剧防,就表示這些緩存被復(fù)用;如果是紅色就表示緩存會被重復(fù)創(chuàng)建辫樱,這就表示該處存在性能問題了峭拘。
注意:Xcode9.3之后的版本功能上做的調(diào)整,這些選項已經(jīng)直接集成到 Xcode 里面了
繪制陰影造成的性能問題
不過我們今天主角并不是圓角狮暑,不過殺傷力并不亞于圓角鸡挠,他的名字叫做陰影。
其實這個東西平常都是設(shè)計同學(xué)或者產(chǎn)品同學(xué)搬男,為了增強(qiáng)頁面的視覺效果而添加的拣展。
一般的實現(xiàn)方式是這樣的:
// 這里我們只是給 cell 添加陰影效果,其他什么都不做缔逛,看看陰影對設(shè)備性能的影響
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: ViewController.cellId, for: indexPath)
cell.backgroundColor = UIColor.gray
cell.layer.shadowOffset = CGSize(width: 0, height: 2)
cell.layer.shadowOpacity = 0.5
return cell
}
打開 Xcode 中監(jiān)測離屏渲染的選項之后备埃,展示在界面上是這個效果(Cell全都觸發(fā)了離屏渲染):
[圖片上傳失敗...(image-31dd3-1552466180881)]
然后我們按 Command+I 快捷鍵進(jìn)入 Profile,選擇 CoreAnimation 檢測幀率及CPU褐奴、GPU利用率(本文所用的 iPhone 設(shè)備為 iPhone6s按脚,系統(tǒng)版本為 iOS12.0.1)。
開啟錄制之后敦冬,操作也很簡單辅搬,就只是簡單的上下滑動列表。
可以看到監(jiān)測到的數(shù)據(jù)如下:
[圖片上傳失敗...(image-317498-1552466180881)]
其實從手機(jī)端的體驗感覺還說的過去脖旱,幀率基本能達(dá)到50fps堪遂,卡頓感比較弱,大部分用戶還是可以接受的萌庆。但是這里比較致命的問題是:1. 我們的列表里還沒有填充數(shù)據(jù)溶褪,僅僅是加了個陰影而已啊,加入了業(yè)務(wù)邏輯后践险,幀率肯定會進(jìn)一步降低猿妈。2. GPU 使用率高的可怕,一般情況下捏境,移動設(shè)備上面幾個耗電大戶無非就是 CPU于游、GPU、WiFi 模塊垫言,GPU 占用率這么高贰剥,用戶最直觀的感受就是:我的手機(jī)是不是該換電池了?筷频?蚌成?這對于一個高使用率的 App 來說簡直是不能接受的前痘。
發(fā)現(xiàn)問題之后,其實解決方案也很簡單:將 Cell 的 layer 進(jìn)行光柵化担忧。雖然光柵化也會觸發(fā)離屏渲染芹缔,但是光柵化會將陰影合成到圖片中,cell 重用的時候直接加載圖片即可瓶盛,不必每次都繪制陰影最欠,從而相對的提升性能。
cell.layer.shouldRasterize = true
不過單獨設(shè)置這個屬性是不夠的惩猫,因為默認(rèn)情況下芝硬,光柵化生成的圖片是 @1x 的,而現(xiàn)在 iOS 設(shè)備顯示器基本都是 @2x 或者 @3x轧房,這樣就會造成顯示模糊拌阴,鋸齒感強(qiáng)烈,影響視覺效果奶镶。
layer 有一個屬性就是專門解決這個問題的:
/* The scale at which the layer will be rasterized (when the
* shouldRasterize property has been set to YES) relative to the
* coordinate space of the layer. Defaults to one. Animatable. */
open var rasterizationScale: CGFloat
那么要解決這個問題迟赃,只需要設(shè)置光柵化的默認(rèn)縮放系數(shù)為屏幕顯示倍率即可:
cell.layer.rasterizationScale = UIScreen.main.scale
更改完之后,我們打開 Xcode 中監(jiān)測離屏渲染的選項厂镇,把項目運行到設(shè)備上查看效果纤壁,這次已經(jīng)沒有明顯的離屏渲染了(光柵化觸發(fā)離屏渲染的過程很短,估計沒有捕捉到):
之后我們再次進(jìn)入 Profile剪撬,查看更改完之后的效果:
界面上顯示效果和修改之前一樣摄乒,滑動幀率有所提升,重要的是 GPU 利用率大大降低了残黑,用戶最直觀的體驗就是設(shè)備續(xù)航體驗更加友好了,再也不會被用4000ma·h大電池手機(jī)的朋友嘲笑電池一天幾沖了 [捂臉]斋否。
至此梨水,UICollectionViewCell 陰影效果造成列表滑動卡頓的問題就得以解決了,方案雖然很簡單茵臭,還是需要我們在日常開發(fā)中多注意這類容易引發(fā)性能問題的點疫诽,發(fā)現(xiàn)問題之后能夠及時的優(yōu)化。做好用戶體驗旦委,才能開發(fā)出更加優(yōu)秀 的 App奇徒!