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

      

navbar

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!

+4


source to share


3 answers


When you read Apple's documentation , you can find:

When using the UIVisualEffectView class, avoid alpha values ​​that are less than 1.

enter image description here

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:

enter image description here

Put a shared image with UIImageView

into your viewController's view, for example this image:

enter image description here



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:

enter image description here

Exit during alpha changes:

enter image description here

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:

enter image description here

+2


source


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 UIView​Property​Animator

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.

0


source


Swift 5.0. Just use the code below:

navigationController?.navigationBar.isTranslucent = false

      

It replaces "0.85 alpha" with a solid color with alpha = 1.

0


source







All Articles