Xcode 8.0 - Swift 2.3
I have an internal extension to create blur layer that works great:
internal extension UIView {
/**
Add
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.