众所周知,如果使用以下的两行代码设置图像圆角,是会触发离屏渲染(离屏渲染详解)。
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")
}
}