iOS圖片設置圓角性能問題

一般我們在iOS開發的過程中設置圓角都是如下這樣設置的。

 avatarImageView.clipsToBounds = YES;
 [avatarImageView.layer setCornerRadius:50];

 這樣設置會觸發離屏渲染,比較消耗性能。比如當一個頁面上有十幾頭像這樣設置了圓角
 會明顯感覺到卡頓。

 注意:png圖片UIImageView處理圓角是不會產生離屏渲染的。(ios9.0之后不會離屏渲染,ios9.0之前還是會離屏渲染)。

所有如果要高性能的設置圓角就需要找另外的方法了。下面是我找到的一些方法并寫了一個例子。

IMG_1816.PNG

設置圓角的方法

  • 直接使用setCornerRadius
    這種就是最常用的,也是最耗性能的。

  • setCornerRadius設置圓角之后,shouldRasterize=YES光柵化

    avatarImageView.clipsToBounds = YES;
    [avatarImageView.layer setCornerRadius:50];
    avatarImageView.layer.shouldRasterize = YES;
    avatarImageViewUrl.layer.rasterizationScale=[UIScreen mainScreen].scale;  //UIImageView不加這句會產生一點模糊
    
    shouldRasterize=YES設置光柵化,可以使離屏渲染的結果緩存到內存中存為位圖,
    使用的時候直接使用緩存,節省了一直離屏渲染損耗的性能。
    
    但是如果layer及sublayers常常改變的話,它就會一直不停的渲染及刪除緩存重新
    創建緩存,所以這種情況下建議不要使用光柵化,這樣也是比較損耗性能的。
    
  • 直接覆蓋一張中間為圓形透明的圖片(推薦使用)
    這種方法就是多加了一張透明的圖片,GPU計算多層的混合渲染blending也是會消耗
    一點性能的,但比第一種方法還是好上很多的。

  • UIImage drawInRect繪制圓角
    這種方式GPU損耗低內存占用大,而且UIButton上不知道怎么繪制,可以用
    UIimageView添加個點擊手勢當做UIButton使用。

    UIGraphicsBeginImageContextWithOptions(avatarImageView.bounds.size, NO, [UIScreen mainScreen].scale);
    [[UIBezierPath bezierPathWithRoundedRect:avatarImageView.bounds
                                  cornerRadius:50] addClip];
    [image drawInRect:avatarImageView.bounds];
    avatarImageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    這段方法可以寫在SDWebImage的completed回調里,在主線程異步繪制。
    也可以封裝到UIImageView里,寫了個DSRoundImageView。后臺線程異步繪制,不會阻塞主線程。
    

問題:這種方法圖片很多的話CUP消耗會高,內存占用也會暴增,而且后臺線程繪制會比在主線程繪制占用更多的內存,不知道怎么解決?求大神指教!

  • SDWebImage處理圖片時Core Graphics繪制圓角

      //UIImage繪制為圓角
      int w = imageSize.width;
      int h = imageSize.height;
      int radius = imageSize.width/2;
      
      UIImage *img = image;
      CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
      CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
      CGRect rect = CGRectMake(0, 0, w, h);
      
      CGContextBeginPath(context);
      addRoundedRectToPath(context, rect, radius, radius);
      CGContextClosePath(context);
      CGContextClip(context);
      CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
      CGImageRef imageMasked = CGBitmapContextCreateImage(context);
      img = [UIImage imageWithCGImage:imageMasked];
      
      CGContextRelease(context);
      CGColorSpaceRelease(colorSpace);
      CGImageRelease(imageMasked);
    

    以上代碼我寫成了UIImage的類別:UIImage+DSRoundImage.h
    并在SDWebImage庫里處理image的時候使用類別方法繪制圓角并緩存。


使用Instruments的Core Animation查看性能

  • Color Offscreen-Rendered Yellow
    開啟后會把那些需要離屏渲染的圖層高亮成黃色,這就意味著黃色圖層可能存在性能問題。

  • Color Hits Green and Misses Red
    如果shouldRasterize被設置成YES,對應的渲染結果會被緩存,如果圖層是綠色,就表示這些緩存被復用;如果是紅色就表示緩存會被重復創建,這就表示該處存在性能問題了。

用Instruments測試得

  • 第一種方法,UIImageView和UIButton都高亮為黃色。

  • 第二種方法,UIImageView和UIButton都高亮為綠色

  • 第三種方法,無任何高亮,說明沒離屏渲染。
    這種圓片覆蓋的方法一般只用在底色為純色的時候,如果圓角圖片的父View是張圖片的時候就沒辦法了,而且底色如果是多種顏色的話那要做多張不同顏色的圓片覆蓋。(可以用代碼取底色的顏色值給圓片著色)

  • 第四種方法無任何高亮,說明沒離屏渲染(但是CPU消耗和內存占用會很大)

  • 第五種方法無任何高亮,說明沒離屏渲染,而且內存占用也不大。(暫時感覺是最優方法)


問題回復:

  • 有回復提到還有一種mask方法。
    這種方法比第一種方法其實更卡頓。一次mask發生了兩次離屏渲染和一次主屏渲染。 具體可以參考小心別讓圓角成了你列表的幀數殺手

  • @nerozhao說第四種比第一種更卡。

    我剛在demo里加了個例子測試了一下,第一種能明顯的感覺到卡頓,第四種還是挺順暢
    的,有興趣的可以自己試試看。第四種是解決了離屏渲染GPU的問題。
    

可以用Instruments的 GPU Driver進行測試:

  • Renderer Utilization
    如果這個值超過了~50%,就意味著你的動畫可能對幀率有所限制,很可能因為離屏渲染或者是重繪導致的過度混合。
  • Tiler Utilization
    如果這個值超過了~50%,就意味著你的動畫可能限制于幾何結構方面,也就是在屏幕上有太多的圖層占用了。
Instruments

圖上面一部分是第一種方法的數據,下面一部分是第四種方法的數據。
第一種方法的Renderer Utilization 和 Tiler Utilization 基本在90%左右。幀率在20左右。
第四種方法的Renderer Utilization 和 Tiler Utilization 基本在20%左右。幀率接近60。
幀率越接近60滑動越順暢。

  但是經過跟@nerozhao的討論發現第四種Core Graphics繪制圓角會有大量的內存占用,
  而且每次繪制的時候CUP消耗會很大。

  由于@nerozhao使用了UITableView進行測試,因為UITableView滾動的時候是一直在
  復用的,UIImageView會重復繪制,所以會一直消耗CUP,然后你就能看的明顯的卡頓。

  @nerozhao在UITableView里圖片的繪制在后臺線程進行繪制,解決了卡頓問題,但是
  由于是在后臺線程的異步繪制所以在滾動的時候會看到圖片先是正方形然后再變成圓形。

  而我使用的是UIScrollerView進行的測試,只有第一次繪制的時候會占用CUP資源,
  所以滑動的時候還是挺流暢的,但是內存消耗還是很大。如果是主線程繪制的話會阻塞一
  點時間的主線程,而后臺線程繪制的話內存消耗會更大,特別容易崩潰。

所以第四種方法當圖片特別多的時候很容易Received memory warning導致崩潰


解決問題參考文章

  • UIImage drawInRect繪制圓角內存暴增問題

文章:
內存惡鬼drawRect - 談畫圖功能的內存優化


最后

關于研究過程及各種設置圓角方法的例子測試對比 github源碼
如果我的文章對你有幫助歡迎github Star
如果你有什么問題或者想交流的可以聯系我。QQ:398411773

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容