Dibuja agujero en UIBlurEffect

Xcode 8.0 – Swift 2.3
Tengo una extensión interna para crear una capa de desenfoque que funciona muy bien:

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

La pregunta es: ¿cómo puedo agregar un agujero con forma en la superposition de desenfoque? He hecho muchos bashs:

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

pero el resultado es:

introduzca la descripción de la imagen aquí

No puedo entender por qué el agujero está bien en UIVisualEffectView pero no en _UIVisualEffectBackdropView

ACTUALIZAR

Probé la solución @Arun (con UIBlurEffectStyle.Dark), pero el resultado no es el mismo:

introduzca la descripción de la imagen aquí

ACTUALIZACIÓN 2

Con la solución de @ Dim_ov tengo: introduzca la descripción de la imagen aquí

Para que esto funcione, necesito ocultar _UIVisualEffectBackdropView de esta manera:

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

En iOS 10, debes utilizar la propiedad de mask de UIVisualEffectView lugar de la mask de CALayer .

Vi esto cubierto en las notas de la versión de algunas versiones anteriores de iOS 10 o Xcode 8, pero no puedo encontrar esas notas ahora :). Actualizaré mi respuesta con un enlace apropiado tan pronto como lo encuentre.

Entonces, aquí está el código que funciona en 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; } } 

Versión 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 } } 

ACTUALIZAR

Bueno, fue discusión de foros de desarrolladores de Apple y no notas de lanzamiento de iOS. Pero hay respuestas de los representantes de Apple, así que creo que esta información puede considerarse "oficial".

Un enlace a la discusión: https://forums.developer.apple.com/thread/50854#157782

Enmascarar la capa de una vista de efectos visuales no garantiza que produzca los resultados correctos; en algunos casos, en iOS 9 produciría un efecto que parecía correcto, pero potencialmente procedía del contenido incorrecto. La vista de efectos visuales ya no generará el contenido incorrecto, pero la única forma admitida de enmascarar la vista es usar cornerRadius directamente en la capa de la vista de efectos visuales (que debería producir el mismo resultado que aquí) o usar el visual vista del efecto de la propiedad maskView.

introduzca la descripción de la imagen aquí

Por favor revisa mi código aquí.

extensión interna 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 } 

}

class ViewController: 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. } 

} Con UIBlurEffectStyle.Light introduzca la descripción de la imagen aquí

Con UIBlurEffectStyle.Dark introduzca la descripción de la imagen aquí

Con UIBlurEffectStyle.ExtraLight introduzca la descripción de la imagen aquí

He tenido el mismo problema en mi código. Después de aplicar la máscara en la vista UIView contiene UIVisualEffectView , el desenfoque desapareció. Sin embargo, podría volverlo si reemplaza el método draw en el padre:

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

No sé por qué funciona exactamente, pero espero que ayude a alguien. El código que solía cortar el agujero:

 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 }