How do you programmatically change the alpha of the UIVisualEffectView in the NavigationBar?
I have a scroll view that I use to control the alpha of other elements, depending on how far the user's view is scrolled.
I set the blur first. The alpha here doesn't seem to accept, in the first place
var effect: UIBlurEffectStyle = .light
if #available(iOS 10.0, *) {
effect = .prominent
}
let blurView = UIVisualEffectView(effect: UIBlurEffect(style: effect))
blurView.alpha = 0
if let navigationBar = navigationController?.navigationBar {
blurView.frame = navigationBar.bounds
}
self.blurView = blurView
Then in mine UIScrollViewDelegate
:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var alpha = (scrollView.contentOffset.y / 200)
alpha = alpha > 1.0 ? 1.0 : alpha
blurView?.alpha = alpha
}
This doesn't work either.
Any advice on how to achieve this effect? Thank!
source to share
When you read Apple's documentation , you can find:
When using the UIVisualEffectView class, avoid alpha values that are less than 1.
In fact, you should also receive a warning:
[Warning] <UIVisualEffectView 0x7af7a3b0> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
PS: Note that I mean that you can change the alpha of the visuals, but the view might just look partially transparent, not blurry.
If you don't believe in this documentation, you can check my little example below for an empty project, and you can see that if you change the alpha value, your effect stops working correctly:
(You can find the entire project below in my GitHUB repository here )
Prepare in an empty project a navigation controller connected to a viewController like this:
Put a shared image with UIImageView
into your viewController's view, for example this image:
class ViewController: UIViewController {
var blurView: UIVisualEffectView!
var effect: UIBlurEffectStyle = .light
var blurEffect : UIBlurEffect!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// This code make more transparent our navigation
if let navigationBar = navigationController?.navigationBar {
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
navigationBar.tintColor = .white
let textAttributes = [NSForegroundColorAttributeName:UIColor.white]
navigationBar.titleTextAttributes = textAttributes
navigationBar.shadowImage = UIImage()
navigationBar.backgroundColor = UIColor(red: 0.0, green: 0.3, blue: 0.5, alpha: 0.3)
navigationBar.isTranslucent = true
}
}
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 10.0, *) {
effect = .prominent
} else {
// Fallback on earlier versions
}
if let navigationBar = navigationController?.navigationBar {
let noEffectView = UIVisualEffectView.init(frame: navigationBar.bounds)
self.blurEffect = UIBlurEffect(style: effect)
self.blurView = noEffectView
navigationBar.addSubview(self.blurView)
// This line below to don't blur buttons and title
navigationBar.sendSubview(toBack: self.blurView)
// Apply the effect:
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.applyBlur), userInfo: nil, repeats: false)
}
}
func applyBlur() {
print("Apply the blur effect..")
UIView.animate(withDuration: 10.2, animations: {
self.blurView.effect = self.blurEffect
}, completion:{ (finished: Bool) in
// Make test with a simple timer:
Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(self.changeAlpha), userInfo: nil, repeats: true)
})
}
func changeAlpha() {
let startNum:CGFloat = 0.0; let stopNum:CGFloat = 200.0
let randomNum = CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(startNum - stopNum) + min(startNum, stopNum)
var alpha = (randomNum / 200)
alpha = alpha > 1.0 ? 1.0 : alpha
print("we change alpha to : \(alpha)")
self.blurView.alpha = alpha
}
}
Output with blur effect:
Exit during alpha changes:
Decision:
You might not want to change the alpha value, but the amount of blur effect while scrolling , so the solution might be to use a new property fractionComplete
UIViewPropertyAnimator
only available from iOS 10.0 (as suggested in this other SO answer)
import UIKit
@available(iOS 10.0, *)
class ViewController: UIViewController {
var blurView: UIVisualEffectView!
var effect: UIBlurEffectStyle = .light
var blurEffect : UIBlurEffect!
var animator: UIViewPropertyAnimator!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// This code make more transparent our navigation
if let navigationBar = navigationController?.navigationBar {
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
navigationBar.tintColor = .white
let textAttributes = [NSForegroundColorAttributeName:UIColor.white]
navigationBar.titleTextAttributes = textAttributes
navigationBar.shadowImage = UIImage()
navigationBar.backgroundColor = UIColor(red: 0.0, green: 0.3, blue: 0.5, alpha: 0.3)
navigationBar.isTranslucent = true
}
}
override func viewDidLoad() {
super.viewDidLoad()
effect = .prominent
if let navigationBar = navigationController?.navigationBar {
let noEffectView = UIVisualEffectView.init(frame: navigationBar.bounds)
self.blurEffect = UIBlurEffect(style: effect)
self.blurView = noEffectView
navigationBar.addSubview(self.blurView)
// This line below to don't blur buttons and title
navigationBar.sendSubview(toBack: self.blurView)
// Apply the effect:
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.applyBlur), userInfo: nil, repeats: false)
}
}
func applyBlur() {
print("Apply the blur effect..")
animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear)
self.blurView.effect = self.blurEffect
animator.addAnimations {
self.blurView.effect = nil
}
Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(self.changeBlurFraction), userInfo: nil, repeats: true)
}
func changeBlurFraction() {
let startNum:CGFloat = 0.0; let stopNum:CGFloat = 200.0
let randomNum = CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(startNum - stopNum) + min(startNum, stopNum)
var blurFraction = (randomNum / 200)
blurFraction = blurFraction > 1.0 ? 1.0 : blurFraction
print("we change the blur fraction to : \(blurFraction)")
animator.fractionComplete = blurFraction
}
}
As you can see this change, the blur effect is not alpha, and every time you start the timer, you may see a different blur value for your nav box.
The trick is to apply the blur effect and add a new animation to tell the effect to zero, but each time with a new "fraction" to change the animation to that exact blur value.
Output:
source to share
Alpha blending a is UIVisualEffectView
broken with both iOS 10 and layer masking due to an internal change in Apple's implementation.
I would suggest using an object UIViewPropertyAnimator
to go from the actual effect ( .prominent
in your case) to nil
and then manually setting the value fractionComplete
as alpha value. This will give you a much better effect and allow you to interactively change the effect.
source to share