本文為投稿文章,作者:@olinone_龐海礁(Github)
最近,關於圖片圓角的話題討論非常激烈,出現了許多好的文章。恰逢工作需要,用到了大量圓角圖片。然而,系統圓角會導致離屏渲染的問題,出於性能考慮,於是有了圖片圓角渲染工具HJCornerRadius,其最大優勢在於使用簡單,一行搞定圖片圓角
imageview.aliCornerRadius = 5.0f;
核心思想就是使用圓角圖片替換系統圓角。實際使用時,確保layer對象的masksToBounds屬性為NO
imageview.layer.masksToBounds = NO
支持pod方式安裝,如果你在實際使用中遇到什麼問題,歡迎在文章後面跟我留言
pod 'HJCornerRadius', :git => "https://github.com/panghaijiao/HJCornerRadius.git"
當然,本文的目的不是介紹如何使用該工具,而是想跟大家分享開發時用到的幾點設計思想
自觀察的巧妙應用
既然要生成圓角圖片,首先要解決生成時機問題。可能會有朋友想到swizzle類UIImageView的setImage方法,但我個人並不推薦,畢竟Swizzle類方法影響范圍太廣,對於大型開發團隊,出問題後很難排查定位問題所在。定義UIImageView子類?實用性不強!
還記得我在文章《“自釋放”在iOS開發中的應用》 中提到的實現自釋放的三種方式嗎?其中一種方式就是動態屬性觀察者——通過創建一個動態屬性KVO被觀察對象的某一屬性,從而達到自監控的目的。
通過創建動態屬性觀察者HJImageObserver,監聽UIImageView的image屬性,當image發生變化時,能夠立即觸發圓角圖片的生成,從而達到自動實現圓角圖片替換的目的,具體實現可以參考源碼 HJCornerRadius。
RunLoop的適當切換
細心的朋友可能注意到,本工具並不是直接渲染原始Image對象,而是先截取UIImageView視圖Layer生成的Image,然後再做渲染。原因很簡單,因為UIImageView呈現方式涉及多種ContentMode,通過渲染UIImageView視圖Layer生成的圖片可以巧妙的解決UIImageView顯示模式的問題
此外,由於系統原因,對於像UITableViewCell的UIImageView,第一次創建賦圖時,可能無法獲取UIImageView視圖Layer的圖片,此時,可以通過切換異步RunLoop達到延時渲染的目的
眾所周知,截圖渲染的邏輯是可以運行在工作線程,但是本工具並沒有把它們放在工作線程執行,因為放在工作線程會有延遲,從而導致圖片閃爍的現象
具體截圖渲染代碼如下
UIGraphicsBeginImageContextWithOptions(self.originImageView.bounds.size, NO, [UIScreen mainScreen].scale); CGContextRef currnetContext = UIGraphicsGetCurrentContext(); CGContextAddPath(currnetContext, [UIBezierPath bezierPathWithRoundedRect:self.originImageView.bounds cornerRadius:self.cornerRadius].CGPath); CGContextClip(currnetContext); [self.originImageView.layer renderInContext:currnetContext]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); if ([image isKindOfClass:[UIImage class]]) { image.aliCornerRadius = YES; self.originImageView.image = image; } else { dispatch_async(dispatch_get_main_queue(), ^{ [self updateImageView]; }); }
經過實際測試驗證,效果還是比較理想的,即使需要顯示大量圓角圖片,顯示幀數也可以穩定在50幀以上。CPU消耗影響較小,以iPhone6測試顯示,CPU實際消耗上漲不會超過5%,屬於合理范圍之內。