ios - 在 UIBlurEffect 上画洞

标签 ios objective-c swift overlay layer

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
    }
    
}

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

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)

但结果是:

enter image description here

我不明白为什么孔在 UIVisualEffectView 上可以,但在 _UIVisualEffectBackdropView 上不行

更新

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

enter image description here

更新 2

使用@Dim_ov 的解决方案,我有: enter image description here

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

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

最佳答案

在 iOS 10 中,您必须使用 UIVisualEffectViewmask 属性,而不是 CALayermask

我在 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;
    }
}

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
    }
}

更新

好吧,这是 Apple 开发者论坛的讨论,而不是 iOS 发行说明。但是有苹果代表的回答,所以我认为,这个信息可以被认为是“官方”。

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

Masking the layer of a visual effect view is not guaranteed to produce the correct results – in some cases on iOS 9 it would produce an effect that looked correct, but potentially sourced the wrong content. Visual effect view will no longer source the incorrect content, but the only supported way to mask the view is to either use cornerRadius directly on the visual effect view’s layer (which should produce the same result as you are attempting here) or to use the visual effect view’s maskView property.

enter image description here

关于ios - 在 UIBlurEffect 上画洞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40256942/

相关文章:

mysql - 从具有相同列的不同表中获取值到数组中

iphone - 如何禁用 UITextField 中的字母字符?

swift - swift3时区问题

iphone - 从推送打开我的应用程序时执行哪个回调

ios无法执行文本到语音的转换

ios - 离开 iPhone 时在 Apple Watch 上记录和保存锻炼?

swift - 使用索引访问元组(由 libc 定义)

ios - 使用 Alamofire 进行简单的发布请求

ios - 如何使用 iOS Quartz 2D 绘图绘制这个复杂的形状?

ios - 在单元格中编辑 UITextView 时,iOS 7 中的 UITableView 不会滚动到正确的位置