在UIBlurEffect上画一个洞

Luc*_*nzo 8 overlay objective-c layer ios swift

Xcode 8.0 - Swift 2.3
我有一个内部扩展来创建非常好的模糊层:

internal extension UIView {

    /**
     Add and display on current view a blur effect.
     */
    internal func addBlurEffect(style style: UIBlurEffectStyle = .ExtraLight, atPosition position: Int = -1) -> UIView {
        // Blur Effect
        let blurEffectView = self.createBlurEffect(style: style)
        if position >= 0 {
            self.insertSubview(blurEffectView, atIndex: position)
        } else {
            self.addSubview(blurEffectView)
        }
        return blurEffectView
    }

    internal func createBlurEffect(style style: UIBlurEffectStyle = .ExtraLight) -> UIView {
        let blurEffect = UIBlurEffect(style: style)
        let blurEffectView = UIVisualEffectView(effect: blurEffect)
        blurEffectView.frame = self.bounds
        blurEffectView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        return blurEffectView
    }

}
Run Code Online (Sandbox Code Playgroud)

问题是:如何在模糊叠加中添加形状的孔?我做了很多尝试:

let p = UIBezierPath(roundedRect: CGRectMake(0.0, 0.0, self.viewBlur!.frame.width, self.viewBlur!.frame.height), cornerRadius: 0.0)
p.usesEvenOddFillRule = true
let f = CAShapeLayer()
f.fillColor = UIColor.redColor().CGColor
f.opacity = 0.5
f.fillRule = kCAFillRuleEvenOdd
p.appendPath(self.holePath)
f.path = p.CGPath
self.viewBlur!.layer.addSublayer(f)
Run Code Online (Sandbox Code Playgroud)

但结果是:

在此输入图像描述

我不明白为什么洞是好的,UIVisualEffectView但不是_UIVisualEffectBackdropView

UPDATE

我试过@Arun解决方案(使用UIBlurEffectStyle.Dark),但结果不一样:

在此输入图像描述

更新2

有了@ Dim_ov的解决方案,我有: 在此输入图像描述

为了完成这项工作,我需要以_UIVisualEffectBackdropView这种方式隐藏:

    for v in effect.subviews {
        if let filterView = NSClassFromString("_UIVisualEffectBackdropView") {
            if v.isKindOfClass(filterView) {
                v.hidden = true
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

Dim*_*_ov 10

在iOS 10中,你必须使用maskproperty UIVisualEffectView而不是CALayers mask.

对于iOS 10或Xcode 8的早期版本,我在发行说明中看到了这一点,但我现在找不到这些注释:).我一找到它就会用适当的链接更新我的答案.

所以这是在iOS 10/Xcode 8中运行的代码:

class ViewController: UIViewController {
    @IBOutlet var blurView: UIVisualEffectView!

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        updateBlurViewHole()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        updateBlurViewHole()
    }

    func updateBlurViewHole() {
        let maskView = UIView(frame: blurView.bounds)
        maskView.clipsToBounds = true;
        maskView.backgroundColor = UIColor.clear

        let outerbezierPath = UIBezierPath.init(roundedRect: blurView.bounds, cornerRadius: 0)
        let rect = CGRect(x: 150, y: 150, width: 100, height: 100)
        let innerCirclepath = UIBezierPath.init(roundedRect:rect, cornerRadius:rect.height * 0.5)
        outerbezierPath.append(innerCirclepath)
        outerbezierPath.usesEvenOddFillRule = true

        let fillLayer = CAShapeLayer()
        fillLayer.fillRule = kCAFillRuleEvenOdd
        fillLayer.fillColor = UIColor.green.cgColor // any opaque color would work
        fillLayer.path = outerbezierPath.cgPath
        maskView.layer.addSublayer(fillLayer)

        blurView.mask = maskView;
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 2.3版本:

class ViewController: UIViewController {
    @IBOutlet var blurView: UIVisualEffectView!

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        updateBlurViewHole()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        updateBlurViewHole()
    }

    func updateBlurViewHole() {
        let maskView = UIView(frame: blurView.bounds)
        maskView.clipsToBounds = true;
        maskView.backgroundColor = UIColor.clearColor()

        let outerbezierPath = UIBezierPath.init(roundedRect: blurView.bounds, cornerRadius: 0)
        let rect = CGRect(x: 150, y: 150, width: 100, height: 100)
        let innerCirclepath = UIBezierPath.init(roundedRect:rect, cornerRadius:rect.height * 0.5)
        outerbezierPath.appendPath(innerCirclepath)
        outerbezierPath.usesEvenOddFillRule = true

        let fillLayer = CAShapeLayer()
        fillLayer.fillRule = kCAFillRuleEvenOdd
        fillLayer.fillColor = UIColor.greenColor().CGColor
        fillLayer.path = outerbezierPath.CGPath
        maskView.layer.addSublayer(fillLayer)

        blurView.maskView = maskView
    }
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

嗯,这是Apple Developer论坛的讨论,而不是iOS发行说明.但是,我认为Apple的代表有答案,这些信息可能被视为"官方".

讨论链接:https://forums.developer.apple.com/thread/50854#157782

屏蔽视觉效果视图的图层并不能保证产生正确的结果 - 在某些情况下,在iOS 9上,它会产生看起来正确的效果,但可能会产生错误的内容.视觉效果视图将不再提供不正确的内容,但屏蔽视图的唯一受支持的方法是直接在视觉效果视图的图层上使用cornerRadius(这应该产生与您在此处尝试相同的结果)或使用视觉效果效果视图的maskView属性.

在此输入图像描述