【问题标题】:Draw hole on UIBlurEffect在 UIBlurEffect 上画洞
【发布时间】:2017-03-08 12:06:52
【问题描述】:

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)

但结果是:

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

更新

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

更新 2

使用@Dim_ov 的解决方案,我有:

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

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

【问题讨论】:

    标签: ios objective-c swift overlay layer


    【解决方案1】:

    请在此处查看我的代码

    内部扩展 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
    }
    

    }

    类视图控制器:UIViewController {

    @IBOutlet weak var blurView: UIImageView!
    
    var blurEffectView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    
        blurEffectView = blurView.addBlurEffect(style: UIBlurEffectStyle.Light, atPosition: 0)
    
    
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        let outerbezierPath = UIBezierPath.init(roundedRect: self.blurEffectView.frame, cornerRadius: 0)
        let rect = CGRectMake(150, 150, 100, 100)
        let innerCirclepath = UIBezierPath.init(roundedRect:rect, cornerRadius:rect.height * 0.5)
        outerbezierPath.appendPath(innerCirclepath)
        outerbezierPath.usesEvenOddFillRule = false
        let fillLayer = CAShapeLayer()
        fillLayer.fillRule = kCAFillRuleEvenOdd
        fillLayer.fillColor = UIColor.blackColor().CGColor
        fillLayer.path = outerbezierPath.CGPath
        self.blurEffectView.layer.mask = fillLayer
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    } 使用 UIBlurEffectStyle.Light

    使用 UIBlurEffectStyle.Dark

    使用 UIBlurEffectStyle.ExtraLight

    【讨论】:

    • 看起来正是我已经做过的......我必须看到圆圈内没有模糊的视图
    • 我试过你的解决方案,但我有不同的输出,看看更新
    • 我无法得到相同的结果。我在 Xcode8 上使用 swift 2.3
    • 我使用的是 Xcode 7,swift 2.2。当我签入 xocde8,swift 2.3 时,它显示的结果有点不同。
    • 该死..如果您找到任何解决方案,请通知我!
    【解决方案2】:

    在 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

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

    【讨论】:

    • @LucaDavanzo,我添加了非常适合我的 Swift 2.3 版本(iOS 10.1)。您可以分享您的非工作版本的屏幕截图吗?
    • 我用屏幕截图和破解/解决方案更新了问题
    • 顺便说一句,感谢您指出解决方案,我会等待链接,)
    • @LucaDavanzo,添加了链接。抱歉耽搁了。这是在 Apple Dev 论坛上讨论的,而不是在发行说明中。这就是为什么我找了这么久都找不到的原因
    【解决方案3】:

    我的代码中遇到了同样的问题。在包含UIVisualEffectViewUIView 上应用蒙版后,模糊消失了。但是,我可以通过覆盖父级上的 draw 方法将其转回:

    override func draw(_ rect: CGRect) {
        super.draw(rect)
    }
    

    我不知道它为什么完全有效,但希望它可以帮助某人。我用来切洞的代码:

    private func makeViewFinderMask() -> CAShapeLayer {
    
        let path = UIBezierPath(rect: bounds)
        let croppedPath = UIBezierPath(roundedRect: viewFinderBounds(), cornerRadius: viewFinderRadius)
        path.append(croppedPath)
        path.usesEvenOddFillRule = true
    
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        mask.fillRule = kCAFillRuleEvenOdd
        return mask
    }
    

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多