UIImageView設定圓角不觸發離屏渲染的方法
阿新 • • 發佈:2019-01-31
眾所周知,如果使用以下的兩行程式碼設定影象圓角,是會觸發離屏渲染(離屏渲染詳解)。
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")
}
}