如何以编程方式更改 NavigationBar 中 UIVisualEffectView 的 alpha?

Posted

技术标签:

【中文标题】如何以编程方式更改 NavigationBar 中 UIVisualEffectView 的 alpha?【英文标题】:How do you programmatically change the alpha of a UIVisualEffectView in a NavigationBar? 【发布时间】:2017-03-29 17:02:19 【问题描述】:

我有一个滚动视图,我用它来控制其他元素的 alpha,具体取决于用户滚动视图的距离。

首先,我设置了模糊视图。 这里的 Alpha 似乎不需要,首先

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

然后在我的UIScrollViewDelegate:

func scrollViewDidScroll(_ scrollView: UIScrollView) 
        var alpha = (scrollView.contentOffset.y / 200)
        alpha = alpha > 1.0 ? 1.0 : alpha

        blurView?.alpha = alpha

这似乎也不起作用。

关于如何实现这种效果的任何建议?谢谢!

【问题讨论】:

看起来好像您已将 blurView 添加到任何地方的视图层次结构中。 我不是,真的。但它出现了,几天来我一直觉得这很奇怪。知道为什么吗? 【参考方案1】:

当您阅读Apple documentation 时,您会发现:

当使用 UIVisualEffectView 类时,避免 alpha 值 小于 1。

事实上你也应该得到一个警告:

[Warning] <UIVisualEffectView 0x7af7a3b0> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.

P.S.: 注意,我的意思是你可以改变视觉效果的 alpha,但视图可能看起来只是部分透明而不是模糊。

如果你不相信这个文档,你可以在一个空白项目中测试我下面的小例子,你可以看到,如果你改变 alpha 值,你的效果会停止正常工作:

(你可以在我的GitHUB仓库here找到下面的所有项目)

在一个空白项目中准备一个连接到 viewController 的 navigationController,如下所示:

将带有UIImageView 的通用图像放到您的viewController 的视图中,如下图:

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
    

带有模糊效果的输出:

alpha 变化期间的输出:

解决办法:

可能您不想更改 alpha 值,而是滚动期间的模糊效果量,因此解决方案可能是使用 UIViewPropertyAnimator 的新 fractionComplete 属性,仅可从iOS 10.0(如 this 其他 SO 回答中所建议)

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
    

正如您所看到的,这改变了模糊效果而不是 alpha,每次触发计时器时,您都可以看到导航栏的模糊值不同。

诀窍是应用模糊效果并添加新动画以将效果报告为 nil,但每次都使用新的“分数”将动画更改为确切的模糊值。

输出:

【讨论】:

GitHUB 项目here 感谢亚历山德罗的详尽回答【参考方案2】:

斯威夫特 5.0。只需使用以下代码:

navigationController?.navigationBar.isTranslucent = false

它用 alpha = 1 的纯色更改“0.85 alpha 效果”。

【讨论】:

【参考方案3】:

由于 Apple 对实现的内部更改,UIVisualEffectView 的 Alpha 混合自 iOS 10 以及图层蒙版被破坏。

我建议使用UIView​Property​Animator 对象将实际效果(在您的情况下为.prominent)转换为nil,然后手动将fractionComplete 设置为alpha 值。它将为您提供更好的效果,并允许您以交互方式更改效果。

【讨论】:

以上是关于如何以编程方式更改 NavigationBar 中 UIVisualEffectView 的 alpha?的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式更改导航栏标题

如何以编程方式设置 UINavigationbar 的背景颜色?

Swift - UINavigationController 栏颜色不会以编程方式更改

以编程方式设置图像的大小

如何以每页为基础更改我的 NavigationBar 的背景图像?

UIScrollView.size = view.size - allAdditionalBars.size(如 TabBar 或 NavigationBar)以编程方式