我有一个内部扩展来创建效果很好的模糊层:
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中没有
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或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: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
嗯,这是Apple Developer论坛的讨论,而不是iOS发行说明.但是,我认为Apple的代表有答案,这些信息可能被视为“官方”.
讨论链接: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.