在 UIBlurEffect 上画洞
Draw hole on UIBlurEffect
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
}
}
}
请在此处检查我的代码
内部扩展 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.
}
}
随着 UIBlurEffectStyle.Light
与UIBlurEffectStyle.Dark
与UIBlurEffectStyle.ExtraLight
在 iOS 10 中你必须使用 UIVisualEffectView
的 mask
属性 而不是 CALayer
的 mask
.
我在 iOS 10 或 Xcode 8 的一些早期测试版的发行说明中看到了这一点,但我现在找不到那些说明 :)。我会在找到答案后立即用适当的 link 更新我的答案。
所以这是在 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 发行说明。但是有苹果代表的回答所以我觉得,这个信息可以考虑"official".
A link 讨论: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.
我的代码中遇到了同样的问题。在我对包含 UIVisualEffectView
的 UIView
应用蒙版后,模糊消失了。但是,我可以通过覆盖父级上的 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
}
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
}
}
}
请在此处检查我的代码
内部扩展 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.
}
}
随着 UIBlurEffectStyle.Light
与UIBlurEffectStyle.Dark
与UIBlurEffectStyle.ExtraLight
在 iOS 10 中你必须使用 UIVisualEffectView
的 mask
属性 而不是 CALayer
的 mask
.
我在 iOS 10 或 Xcode 8 的一些早期测试版的发行说明中看到了这一点,但我现在找不到那些说明 :)。我会在找到答案后立即用适当的 link 更新我的答案。
所以这是在 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 发行说明。但是有苹果代表的回答所以我觉得,这个信息可以考虑"official".
A link 讨论: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.
我的代码中遇到了同样的问题。在我对包含 UIVisualEffectView
的 UIView
应用蒙版后,模糊消失了。但是,我可以通过覆盖父级上的 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
}