1. 程式人生 > >UIImageView設定圓角不觸發離屏渲染的方法

UIImageView設定圓角不觸發離屏渲染的方法

眾所周知,如果使用以下的兩行程式碼設定影象圓角,是會觸發離屏渲染(離屏渲染詳解)。

imageView.layer.cornerRadius = 10  
imageView.layer.masksToBounds = true

如果是在一個tableview中,每一個cell有這樣一個圓角圖片,那麼在滾動時肯定會嚴重掉幀。所以以上的方法只適合在靜態介面(不需要滾動互動)中使用,或者在當前頁面中只有一個圓角圖片時。

那如果需要在tableview中設定多個圓角圖片呢?既然我們要避免讓GPU觸發離屏,那麼只能把兵符交給CPU,雖然CPU對圖形的處理能力不及GPU,但由於這種處理的難度不大,且代價肯定遠小於上下文切換。具體方法是在給UIImageView賦值UIImage時,將UIImage進行裁剪再賦值。不多說了,上程式碼:

import UIKit

extension UIImageView {

    private struct aKey {
        static var imageObserverKey = "imageObserver"
    }

    var aCornerRadius:CGFloat {

        get {
            return self.imageObserver().cornerRadius
        }

        set {
            self.imageObserver().cornerRadius = newValue
        }
    }

    private
func imageObserver() -> XYImageObserver{ var observer = objc_getAssociatedObject(self, &aKey.imageObserverKey) as? XYImageObserver if observer == nil { observer = XYImageObserver.init(imageView: self) objc_setAssociatedObject(self, &aKey.imageObserverKey, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } return
observer!; } } // MARK: - extension UIImage extension UIImage { private struct aKey { static var aCornerRadiuskey = "aCornerRadius" } var aCornerRadius:Bool { get { if let value = objc_getAssociatedObject(self, &aKey.aCornerRadiuskey) as? Bool { return value } else { return false } } set { objc_setAssociatedObject(self, &aKey.aCornerRadiuskey, newValue as Bool, .OBJC_ASSOCIATION_COPY_NONATOMIC) } } } // MARK: - 監聽者 class XYImageObserver: NSObject { var originImageView:UIImageView! var originImage:UIImage? var cornerRadius:CGFloat{ willSet { if self.cornerRadius != newValue { self.cornerRadius = newValue if newValue > 0 { self.cutImageView() } } } } init(imageView:UIImageView) { self.cornerRadius = 0 super.init() self.originImageView = imageView // 新增監聽 imageView.addObserver(self, forKeyPath: "image", options: .new, context: nil) } // 監聽回撥 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "image" { if let newImage = change?[NSKeyValueChangeKey.init(rawValue: "new")] { if (newImage as AnyObject).isKind(of:UIImage.classForCoder()) == false || ((newImage as? UIImage)?.aCornerRadius)! { return } if self.originImageView.aCornerRadius > 0 { self.cutImageView() } } } } // 裁剪圖片 private func cutImageView() { if let image = self.originImageView.image { self.originImage = image // 開始裁剪 UIGraphicsBeginImageContextWithOptions(self.originImageView.bounds.size, false, UIScreen.main.scale) let currentContext = UIGraphicsGetCurrentContext() let path = UIBezierPath.init(roundedRect: self.originImageView.bounds, cornerRadius: self.cornerRadius).cgPath currentContext?.addPath(path) currentContext?.clip() if currentContext != nil { self.originImageView.layer.render(in: currentContext!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() if image?.isKind(of:UIImage.classForCoder()) == true { image?.aCornerRadius = true; self.originImageView.image = image; } else { DispatchQueue.main.async { self.cutImageView() } } } } } deinit { self.originImageView.removeObserver(self, forKeyPath: "image") } }