【问题标题】:Why does blurred CALayer on top of UIView's layer let view show through?为什么 UIView 图层顶部的模糊 CALayer 让视图显示出来?
【发布时间】:2020-12-03 20:50:58
【问题描述】:

我一直在寻找 UIVisualEffectView 模糊效果的替代品,让我可以改变模糊半径。我考虑过使用 Core Image 过滤器自己创建变量模糊。但是,我对核心图像过滤器有点不习惯。 (我最后一次使用它们是在 Objective-C 中。)

我在 SO 上找到了这个答案,它提供了 UIView 的扩展,它捕获视图及其子视图的合成内容,生成模糊版本,并将其作为图像视图添加到视图顶部:https://stackoverflow.com/a/56521521/205185

我决定将其修改为 UIView 的自定义子类,在视图层的顶部添加一个模糊层。

结果是模糊层的内容大多是透明的,并没有很模糊底层视图。我在视图中添加了一个图像视图,它的内容似乎一点也不模糊。

Here is a link 我创建的项目。

当我生成模糊图像时,我将它安装为视图层顶部的层内容,并将其复制到下面的单独图像视图中。单独的图像视图看起来与我想要的结果完全一样,但是顶部图层的视图几乎没有可见的变化。大多数情况下,下面的原始图像显示出来。

原始合成视图如下所示:

在下面的屏幕截图中,顶部图像是顶部带有模糊层(模糊半径 10)的合成图像,底部图像是安装在单独图像视图中的模糊输出:

您可以从图像视图版本中看到,模糊图像是完全不透明的。绿色背景随处可见。视图控制器的白色背景都没有渗出。然而,在顶部带有模糊层的顶部自定义视图中,文本的清晰边缘以及图像视图和蓝色框视图上的边框仍然可见。 BlurView 顶部有模糊层的外观几乎没有改变。这是为什么呢?

BlurView 类中的两个关键函数如下:

func applyBlur() {
    let context = CIContext(options: nil)
    self.makeBlurredImage(with: blurLevel, context: context, completed: { processedImage in
        self.blurLayer.contents = processedImage.cgImage
        self.updateBlurImage?(processedImage)
    })
}

private func makeBlurredImage(with level: CGFloat, context: CIContext, completed: @escaping (UIImage) -> Void) {
    // screen shot
    layer.isOpaque = true

    UIGraphicsBeginImageContextWithOptions(self.frame.size, false, 1)
    self.layer.render(in: UIGraphicsGetCurrentContext()!)
    let resultImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    let beginImage = CIImage(image: resultImage)


    //Clamp the image
    guard let clampFilter = CIFilter(name: "CIAffineClamp") else {
        fatalError()
    }
    clampFilter.setValue(beginImage, forKey: kCIInputImageKey)
    clampFilter.setValue(CGAffineTransform.identity, forKey: kCIInputTransformKey)
    var output = clampFilter.outputImage

    let blurFilter = CIFilter(name: "CIGaussianBlur")!
    blurFilter.setValue(level, forKey: kCIInputRadiusKey)
    blurFilter.setValue(output, forKey: kCIInputImageKey)


    let cropFilter = CIFilter(name: "CICrop")!
    cropFilter.setValue(blurFilter.outputImage, forKey: kCIInputImageKey)
    cropFilter.setValue(CIVector(cgRect: beginImage!.extent), forKey: "inputRectangle")

    output = cropFilter.outputImage
    var cgimg: CGImage?
    var extent: CGRect?

    let global = DispatchQueue.global(qos: .userInteractive)

    global.async {
        extent = output!.extent
        cgimg = context.createCGImage(output!, from: extent!)!
        let processedImage = UIImage(cgImage: cgimg!)

        DispatchQueue.main.async {
            completed(processedImage)
        }
    }
}

makeBlurredImage() 函数异步运行,并在模糊图像可用时调用闭包。

applyBlur() 函数调用makeBlurredImage(),它传递的闭包将生成的模糊图像安装在位于 BlurView 内容层之上的层中。

我希望 BlurView 的外观与下面的图像视图的外观完全相同。我被难住了。

【问题讨论】:

    标签: ios swift calayer core-image


    【解决方案1】:

    我找到了解决方案,但不清楚为什么我的项目中的代码会表现出我描述的行为。

    我在视图的 frame 属性上有一个 didSet 方法,用于设置图层的边界。这让它能够适应旋转和调整大小。

    我将模糊层设为惰性变量。下面是这段代码的样子:

    lazy var blurLayer: CALayer = {
        let newLayer = CALayer()
        self.layer.addSublayer(newLayer)
        newLayer.isOpaque = true
        return newLayer
    }()
    
    override var frame: CGRect {
        didSet {
            blurLayer.frame = self.bounds
        }
    }
    

    看起来框架是在视图添加到视图层次结构之前设置的,因此在设置框架时将层添加为子层会使事情变得混乱。如果我更改我的惰性 var blurLayer 以删除添加子层:

    lazy var blurLayer: CALayer = {
        let newLayer = CALayer()
        //self.layer.addSublayer(newLayer)
        //newLayer.isOpaque = true
        return newLayer
    }()
    

    然后在 awakeFromNib() 中执行它,它可以工作:

    override func awakeFromNib() {
        self.layer.addSublayer(blurLayer)
        blurLayer.isOpaque = true
        applyBlur()
    }
    

    正如我所说,我不清楚为什么,但它确实有效。

    【讨论】:

    • 有谁知道为什么我设置图层的原始代码与更新后的代码不同?
    猜你喜欢
    • 1970-01-01
    • 2022-12-17
    • 1970-01-01
    • 2015-09-23
    • 2012-07-29
    • 2011-07-11
    • 2019-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多