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")
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值