在UIBlurEffect上画一个洞 [英] Draw hole on UIBlurEffect

查看:237
本文介绍了在UIBlurEffect上画一个洞的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Xcode 8.0 - Swift 2.3

我有一个内部扩展来创建效果很好的模糊图层:

 内部扩展UIView {

/ **
在当前视图中添加并显示模糊效果。
* /
internal func addBlurEffect(样式样式:UIBlurEffectStyle = .ExtraLight,atPosition position:Int = -1) - > UIView {
//模糊效果
让blurEffectView = self.createBlurEffect(style:style)
如果position> = 0 {
self.insertSubview(blurEffectView,atIndex:position)
} else {
self.addSubview(blurEffectView)
}
返回blurEffectView
}

内部函数createBlurEffect(样式样式:UIBlurEffectStyle = .ExtraLight) - > UIView {
let blurEffect = UIBlurEffect(style:style)
let blurEffectView = UIVisualEffectView(effect:blurEffect)
blurEffectView.frame = self.bounds
blurEffectView.autoresizingMask = [.FlexibleWidth ,.FlexibleHeight]
返回blurEffectView
}

}

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

 让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 ()。CGGolor
f.opacity = 0.5
f.fillRule = kCAFillRuleEvenOdd
p.appendPath(self.holePath)
f.path = p.CGPath
self .viewBlur!.layer.addSublayer(f)

但结果是:





我无法理解为什么 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
}
}
}


解决方案

在iOS 10中,您必须使用 UIVisualEffectView的掩码属性而不是 CALayer 掩码



我看到了他在iOS 10或Xcode 8的早期测试版的发行说明中有所涉及,但我现在找不到这些注释:)。我会在找到后立即用适当的链接更新我的答案。



所以这里的代码适用于iOS 10 / Xcode 8:

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

覆盖func viewDidAppear(_ animated:Bool){
super.viewDidAppear(animated)

updateBlurViewHole()
}

覆盖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,宽度:100,高度: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 //任何不透明颜色都可以使用
fillLayer.path = outerbezierPath.cgPath
maskView.layer.addSublayer(fillLayer)

blurView.mask = maskView;
}
}

Swift 2.3版本:

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

覆盖func viewDidAppear(动画:Bool){
super.viewDidAppear(动画)

updateBlurViewHole()
}

覆盖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,宽度:100,高度: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 Developer论坛的讨论和不是iOS发行说明。但是Apple的代表也有答案,所以我认为,这些信息可能被视为官方。



讨论的链接:


Xcode 8.0 - Swift 2.3
I have an internal extension to create blur layer that works great:

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
    }

}

Question is: how can I add a shaped hole in blur overlay? I have made many attempts:

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)

but result is:

I can't understand why hole is ok on UIVisualEffectView but not in _UIVisualEffectBackdropView

UPDATE

I've tryied @Arun solution (with UIBlurEffectStyle.Dark), but result is not the same:

UPDATE 2

With @Dim_ov's solution I have:

In order to make this work I need to hide _UIVisualEffectBackdropViewin this way:

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

解决方案

In iOS 10 you have to use mask property of UIVisualEffectView instead of CALayer's mask.

I saw this covered in the release notes for some early betas of iOS 10 or Xcode 8, but I can not find those notes now :). I'll update my answer with a proper link as soon as I find it.

So here is the code that works in 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 version:

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

UPDATE

Well, it was Apple Developer forums discussion and not iOS release notes. But there are answers from Apple's representatives so I think, this information may be considered "official".

A link to the discussion: 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.

这篇关于在UIBlurEffect上画一个洞的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆