为什么UIGraphicsGetCurrentContext在UIGraphicsBeginImageContext之后返回nil

问题描述

我正在遵循一个代码示例来制作模糊的UILabel https://stackoverflow.com/a/62224908/2226315

我的要求是在标签初始化后使标签模糊,而不是在运行时调用junit-jupiter-engine方法。但是,当我尝试在标签初始化后调用blur时,从blur返回的值是UIGraphicsGetCurrentContext,因此出现“致命错误:在展开可选值时意外发现nil”

nil

我尝试将代码分别添加到以下所有位置,现在可以获取上下文,但是它不会产生预期的模糊效果

UIGraphicsBeginImageContext(bounds.size)
print("DEBUG: bounds.size",bounds.size)
self.layer.render(in: UIGraphicsGetCurrentContext()!) // <- return nil
var image = UIGraphicsGetimageFromCurrentimageContext()
UIGraphicsEndImageContext()
print("DEBUG: image image",image)

完整代码

override func layoutSubviews() {
    super.layoutSubviews()
    self.blur()
}

// OR
    
override func draw(_ rect: CGRect) {
    super.draw(rect)
    self.blur()
}

参考

更新

基于“ Eugene Dudnyk”答案的用法

class BlurredLabel: UILabel {

    func blur(_ blurRadius: Double = 2.5) {        
        let blurredImage = getBlurryImage(blurRadius)
        let blurredImageView = UIImageView(image: blurredImage)
        blurredImageView.translatesAutoresizingMaskIntoConstraints = false
        blurredImageView.tag = 100
        blurredImageView.contentMode = .center
        blurredImageView.backgroundColor = .white
        addSubview(blurredImageView)
        NSLayoutConstraint.activate([
            blurredImageView.centerXAnchor.constraint(equalTo: centerXAnchor),blurredImageView.centerYAnchor.constraint(equalTo: centerYAnchor)
        ])
    }

    func unblur() {
        subviews.forEach { subview in
            if subview.tag == 100 {
                subview.removeFromSuperview()
            }
        }
    }

    private func getBlurryImage(_ blurRadius: Double = 2.5) -> UIImage? {
        UIGraphicsBeginImageContext(bounds.size)
        layer.render(in: UIGraphicsGetCurrentContext()!)
        guard let image = UIGraphicsGetimageFromCurrentimageContext(),let blurFilter = CIFilter(name: "CIGaussianBlur") else {
            UIGraphicsEndImageContext()
            return nil
        }
        UIGraphicsEndImageContext()

        blurFilter.setDefaults()

        blurFilter.setValue(CIImage(image: image),forKey: kCIInputimageKey)
        blurFilter.setValue(blurRadius,forKey: kCIInpuTradiusKey)

        var convertedImage: UIImage?
        let context = CIContext(options: nil)
        if let blurOutputimage = blurFilter.outputimage,let cgImage = context.createCGImage(blurOutputimage,from: blurOutputimage.extent) {
            convertedImage = UIImage(cgImage: cgImage)
        }

        return convertedImage
    }
}

解决方法

这是一个更好的解决方案-而不是检索模糊的图像,只需让标签本身模糊即可。

当您需要使其模糊时,请设置label.isBluring = true。 另外,此解决方案的性能更好,因为它可以重复使用相同的上下文,并且不需要图像视图。

class BlurredLabel: UILabel {
    
    var isBlurring = false {
        didSet {
            setNeedsDisplay()
        }
    }

    var blurRadius: Double = 2.5 {
        didSet {
            blurFilter?.setValue(blurRadius,forKey: kCIInputRadiusKey)
        }
    }

    lazy var blurFilter: CIFilter? = {
        let blurFilter = CIFilter(name: "CIGaussianBlur")
        blurFilter?.setDefaults()
        blurFilter?.setValue(blurRadius,forKey: kCIInputRadiusKey)
        return blurFilter
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        layer.isOpaque = false
        layer.needsDisplayOnBoundsChange = true
        layer.contentsScale = UIScreen.main.scale
        layer.contentsGravity = .center
        isOpaque = false
        isUserInteractionEnabled = false
        contentMode = .redraw
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func display(_ layer: CALayer) {
        let bounds = layer.bounds
        guard !bounds.isEmpty && bounds.size.width < CGFloat(UINT16_MAX) else {
            layer.contents = nil
            return
        }
        UIGraphicsBeginImageContextWithOptions(layer.bounds.size,layer.isOpaque,layer.contentsScale)
        if let ctx = UIGraphicsGetCurrentContext() {
            self.layer.draw(in: ctx)
        
            var image = UIGraphicsGetImageFromCurrentImageContext()?.cgImage
            if isBlurring,let cgImage = image {
                blurFilter?.setValue(CIImage(cgImage: cgImage),forKey: kCIInputImageKey)
                let ciContext = CIContext(cgContext: ctx,options: nil)
                if let blurOutputImage = blurFilter?.outputImage,let cgImage = ciContext.createCGImage(blurOutputImage,from: blurOutputImage.extent) {
                    image = cgImage
                }
            }
            layer.contents = image
        }
        UIGraphicsEndImageContext()
    }
}