UINavigationBar 在推送时更改颜色

Posted

技术标签:

【中文标题】UINavigationBar 在推送时更改颜色【英文标题】:UINavigationBar change colors on push 【发布时间】:2016-10-25 14:49:28 【问题描述】:

我在 UINavigationBar 在不同的视图中使用了 2 种不同的条形颜色。我在两个视图中都使用该方法更改颜色:

override func viewWillAppear(animated: Bool) 
    super.viewWillAppear(animated)
    self.navigationController?.navigationBar.barTintColor = COLOR

当我点击后退按钮颜色变化不顺畅(你可以看到最后一秒闪烁)。

但如果只是向后滑动视图而不是点击返回按钮,一切都会好起来的。

如何在这两种情况下顺利过渡?

【问题讨论】:

【参考方案1】:

要实现这种动画,你应该使用UIViewControllerTransitionCoordinator as Apple documentation 说它是:

采用 UIViewControllerTransitionCoordinator 协议的对象为与视图控制器转换关联的动画提供支持。(...)

所以每个UIViewController 都有自己的transitionController。要得到这个,你应该打电话给UIViewControllerClass

self.transitionCoordinator()

来自documentation:

返回活动转换协调器对象。

所以要得到你想要的结果,你应该在 viewController transitionCoordinatior 中实现animateAlongsideTransition 方法。当您单击 backButton 并向后滑动时,动画会起作用。

例子:

第一个控制器:

class ViewControllerA: UIViewController 

    override func loadView() 
        super.loadView()
        title = "A"
        view.backgroundColor = .white
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "NEXT", style: .plain, target: self, action: #selector(self.showController))
        setColors()
    

    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)
        animate()
    

    func showController() 
        navigationController?.pushViewController(ViewControllerB(), animated: true)
    

    private func animate() 
        guard let coordinator = self.transitionCoordinator else 
            return
        

        coordinator.animate(alongsideTransition: 
            [weak self] context in
            self?.setColors()
        , completion: nil)
    

    private func setColors() 
        navigationController?.navigationBar.tintColor = .black
        navigationController?.navigationBar.barTintColor = .red
    

第二控制器:

class ViewControllerB : UIViewController 

    override func loadView() 
        super.loadView()
        title = "B"
        view.backgroundColor = .white
        setColors()
    

    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)
        animate()
    

    override func willMove(toParentViewController parent: UIViewController?)  // tricky part in ios 10
        navigationController?.navigationBar.barTintColor = .red //previous color
        super.willMove(toParentViewController: parent)
    

    override func viewDidAppear(_ animated: Bool) 
        super.viewDidAppear(animated)
        navigationController?.navigationBar.barTintColor = .blue
    

    private func animate() 
        guard let coordinator = self.transitionCoordinator else 
            return
        
        coordinator.animate(alongsideTransition: 
            [weak self] context in
            self?.setColors()
        , completion: nil)
    

    private func setColors()
        navigationController?.navigationBar.tintColor = .black
        navigationController?.navigationBar.barTintColor = .blue
    


更新 iOS 10

在 iOS 10 中,棘手的部分是在 second ViewController 中添加 willMoveTo(parentViewController parent: UIViewController?)。并将 navigationBar tintColor 设置为 previous 控制器的颜色值。此外,在 second ViewControler 中的 viewDidAppear 方法中,将 navigationBar.tintColor 设置为来自 second viewController 的颜色。

看看我的例子project on github

【讨论】:

它对我不起作用,我收到的结果与我的问题相同(向后滑动没问题,点击向后闪烁)。代码中的示例项目:dl.dropboxusercontent.com/u/42855950/NavigationTransition.zip @Vasily 我会检查一下 好的,请检查,如果您找到解决方案更新答案,如果解决方案不正确,请删除 我会告诉你或删除它,你在 Xcode8 上使用 swift 3,对吗? @kamwysoc 谢谢老兄!!您的 iOS 10 更新帮助了我!感谢您节省人们的时间 :thumbsup:【参考方案2】:

我编写了看起来使用起来最舒适的最终解决方案(不需要在自己的视图控制器中使用大量覆盖)。它在 iOS 10 上完美运行,并且可以轻松用于自己的目的。

GitHub

你可以查看GitHub Gist获取完整的类代码和更详细的指南,我不会在这里发布完整的代码,因为 *** 不适合存储大量代码。

用法

为 GitHub 下载 Swift 文件。要使其正常工作,只需使用ColorableNavigationController 而不是UINavigationController 并将所需的子视图控制器采用NavigationBarColorable 协议。

示例:

class ViewControllerA: UIViewController, NavigationBarColorable 
    public var navigationBarTintColor: UIColor?  return UIColor.blue 

    override func viewDidLoad() 
        super.viewDidLoad()

        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Push", style: .plain, target: self, action: #selector(self.showController))
    

    func showController() 
        navigationController?.pushViewController(ViewControllerB(), animated: true)
    


class ViewControllerB: UIViewController, NavigationBarColorable 
    public var navigationBarTintColor: UIColor?  return UIColor.red 


let navigationController = ColorableNavigationController(rootViewController: ViewControllerA())

【讨论】:

【参考方案3】:

这对我有用:

 override func willMove(toParent parent: UIViewController?) 
      super.willMove(toParent: parent)
      navigationController?.navigationBar.barTintColor = previous view controller's navigation bar color
 

【讨论】:

这个解决方案有帮助【参考方案4】:

我只是想知道。出于同样的目的,我使用UINavigationControllerDelegate。在navigationController(_:willShow:) 中,我使用transitionCoordinator?.animate(alongsideTransition:completion:) 开始动画。推送新控制器时效果很好,但 pop 没有。

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) 
  let dst = viewController as! ViewController
  guard animated else 
    navigationController.navigationBar.barTintColor = dst.navigationBarColor
    navigationController.navigationBar.tintColor = dst.tintColor
    navigationController.navigationBar.barStyle = dst.barStyle
    return
  

  navigationController.transitionCoordinator?.animate(alongsideTransition:  context in
    navigationController.navigationBar.barTintColor = dst.navigationBarColor
    navigationController.navigationBar.tintColor = dst.tintColor
    navigationController.navigationBar.barStyle = dst.barStyle
  , completion:  context in
    if context.isCancelled 
      let source = context.viewController(forKey: UITransitionContextViewControllerKey.from) as! ViewController
        navigationController.navigationBar.barTintColor = source.navigationBarColor
        navigationController.navigationBar.tintColor = source.tintColor
        navigationController.navigationBar.barStyle = source.barStyle
    
)

你有什么理由认为它应该适用于推送而不是弹出?

【讨论】:

你弄明白了吗?我正在为完全同样的事情而苦苦挣扎。 不,我放弃了过渡并使用透明导航栏,然后在控制器中创建了一个模拟背景的视图... 我实际上是在我在这里发表评论后几个小时让它工作的。它不漂亮,但它有效。我现在正在为 UIStatusBarStyle 制作动画而苦苦挣扎.. 您是否通过覆盖 willMove(toParentViewController:) 或其他方式使其工作? 在普通超类的 willAppear 中使用 animateAlongside。一切正常,除了使用常规的后退按钮弹出视图。推动有效,向后滑动有效。为了修复它,我将navigationController 子类化,覆盖popViewController,并在那里设置tintcolor 等。问题是向后滑动也会触发此功能。然而,navigationController 有自己的手势识别器用于向后滑动。所以在popViewController 内部做任何事情之前,我会检查那个手势的状态。如果状态为possible,则用户没有向后滑动。

以上是关于UINavigationBar 在推送时更改颜色的主要内容,如果未能解决你的问题,请参考以下文章

更改 UINavigationBar 外观背景图像时状态栏颜色更改

UINavigationBar 不立即更新颜色

更改 UISearchController 的 UINavigationBar 背景颜色

UINavigationBar 无法更改颜色

全局更改 UINavigationBar 条的色调颜色

iOS:删除 UINavigationBar 动画