我该如何实现UINavigationBar的颜色过渡/动画效果?

33

我一直在寻找如何过渡/动画一个UINavigationBarbarTintColor,看到了不同的答案。有些人使用UIView.animateWithDuration,有些人使用CATransition,但最有趣的是像这个使用animate(alongsideTransition animation..的方法,我很喜欢这种方法,但我无法使其正常工作。我做错了什么吗?

许多人指定我可以简单地在viewWillAppear:中使用transitionCoordinator,我已经像下面这样设置了一个全新超小项目:

class RootViewController:UIViewController{ //Only subclassed
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
            self?.setNavigationColors()
            }, completion: nil)
    }
    func setNavigationColors(){
        //Override in subclasses
    }
}

class FirstViewController: RootViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "First"
    }
    override func setNavigationColors(){
        navigationController?.navigationBar.barTintColor = UIColor.white
        navigationController?.navigationBar.tintColor = UIColor.black
        navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.black]
        navigationController?.navigationBar.barStyle = UIBarStyle.default
    }
}
class SecondViewController: RootViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "Second"
    }
    override func setNavigationColors(){
        navigationController?.navigationBar.barTintColor = UIColor.black
        navigationController?.navigationBar.tintColor = UIColor.white
        navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
        navigationController?.navigationBar.barStyle = UIBarStyle.black
    }
}

使用这段代码后,会出现以下情况: First

  • FirstSecond 的 push 转场看起来很完美。所有元素过渡得很好,除了状态栏外,它立即变成了白色。我更想知道如何转换它,但目前我会先接受它。
  • SecondFirst 的 pop 转场完全错误。它保留了 Second 的颜色,直到转场完成。
  • SecondFirst 的拖动转场看起来还不错,只要一直拖到最后。同样地,当我开始拖动时,状态栏立即变成黑色,但我不知道是否可能修复。
  • SecondFirst 的拖动转场,但在中途取消并返回到 Second,则完全出现了问题。它看起来很好,直到 Second 完全控制后,突然将自己更改为 First 的颜色。这不应该发生。

我对我的 RootViewController 进行了一些改进,使其更好。我完全删除了 viewWillAppear:,并用以下内容替换:

class RootViewController:UIViewController{

    override func willMove(toParentViewController parent: UIViewController?) {
        if let last = self.navigationController?.viewControllers.last as? RootViewController{
            if last == self && self.navigationController!.viewControllers.count > 1{
                if let parent = self.navigationController!.viewControllers[self.navigationController!.viewControllers.count - 2] as? RootViewController{
                    parent.setNavigationColors()
                }
            }
        }
    }
    override func viewWillDisappear(_ animated: Bool) {
        if let parent = navigationController?.viewControllers.last as? RootViewController{
            parent.animateNavigationColors()
        }
    }
    override func viewDidAppear(_ animated: Bool) {
        self.setNavigationColors()
    }

    func animateNavigationColors(){
        transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
            self?.setNavigationColors()
            }, completion: nil)
    }
    func setNavigationColors(){
        //Override in subclasses
    }
}

使用这个更新后的代码,我得到了以下结果:Second

几点观察:

  • FirstSecond的过渡是一样的
  • SecondFirst的弹出式过渡现在已经正确地进行动画处理,除了后退箭头、后退文本(和状态栏,但是...)。它们会立即变成黑色。在第一个gif中,你可以看到后退箭头和后退文本也有过渡效果。
  • SecondFirst的拖拽式过渡也有这个问题,当开始时,后退箭头和后退文本会突然瞬间变成黑色。barTint被修复,以防止在取消拖拽时获得错误的颜色。

我做错了什么?我应该怎么做?

我的目标是平稳地过渡所有元素。返回按钮的色调、后退文本、标题、barTint和状态栏。这不可能吗?


我已经寻找这个问题的解决方案数月了。 - mxcl
@mxcl https://dev59.com/2FgQ5IYBdhLWcg3wRR5j#46929155 - Josshad
2个回答

11

在这里输入图片描述

在 iOS 10 上,它的表现不够完美 :(

将您的导航控制器子类化以使用可见视图控制器的状态栏样式:

class MyNavigationController: UINavigationController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return visibleViewController!.preferredStatusBarStyle
    }
}

在根控制器中重写preferredStatusBarStyle属性,并添加设置样式的函数,在弹出动画之前调用:

private var _preferredStyle = UIStatusBarStyle.default;
override var preferredStatusBarStyle: UIStatusBarStyle {
    get {
        return _preferredStyle
    }
    set {
        _preferredStyle = newValue
        self.setNeedsStatusBarAppearanceUpdate()
    }

}


func animateNavigationColors(){
        self.setBeforePopNavigationColors()
        transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
            self?.setNavigationColors()
            }, completion: nil)
    }

func setBeforePopNavigationColors() {
    //Override in subclasses
}

在第一个控制器中:

override func setBeforePopNavigationColors() {
    navigationController?.navigationBar.tintColor = UIColor.white
    navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
    self.preferredStatusBarStyle = UIStatusBarStyle.lightContent
}

override func setNavigationColors(){
    navigationController?.navigationBar.barTintColor = UIColor.white
    navigationController?.navigationBar.tintColor = UIColor.black
    navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black]
    navigationController?.navigationBar.barStyle = UIBarStyle.default
    self.preferredStatusBarStyle = UIStatusBarStyle.default
}

在第二个方案中:

  override func setNavigationColors(){
        navigationController?.navigationBar.barTintColor = UIColor.black
        navigationController?.navigationBar.tintColor = UIColor.white
        navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
        navigationController?.navigationBar.barStyle = UIBarStyle.black
        self.preferredStatusBarStyle = UIStatusBarStyle.lightContent
    }

示例项目:https://github.com/josshad/TestNavBarTransition

该项目是一个关于导航栏过渡效果的示例。

2
虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。仅有链接的答案如果将来其他问题/答案被删除可能会变得无效。 - adiga

2

您可以覆盖 UINavigationController 的 push 和 pop 方法来设置导航栏颜色。我使用了 UINavigationItem 的自定义子类将视图控制器对应的导航栏颜色存储在其导航项中。以下代码适用于 iOS 11,同时适用于全屏和交互式转换:

import UIKit

class NavigationItem: UINavigationItem {
    @IBInspectable public var barTintColor: UIColor?
}

class NavigationController: UINavigationController, UIGestureRecognizerDelegate {
    func applyTint(_ navigationItem: UINavigationItem?) {
        if let item = navigationItem as? NavigationItem {
            self.navigationBar.barTintColor = item.barTintColor
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        applyTint(self.topViewController?.navigationItem)
        self.interactivePopGestureRecognizer?.delegate = self
    }
    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        applyTint(viewController.navigationItem)
        super.pushViewController(viewController, animated: animated)
    }

    override func popViewController(animated: Bool) -> UIViewController? {
        let viewController = super.popViewController(animated: animated)

        applyTint(self.topViewController?.navigationItem)
        return viewController
    }

    override func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? {
        let result = super.popToViewController(viewController, animated: animated)

        applyTint(viewController.navigationItem)
        return result
    }

    override func popToRootViewController(animated: Bool) -> [UIViewController]? {
        let result = super.popToRootViewController(animated: animated)

        applyTint(self.topViewController?.navigationItem)
        return result
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return (otherGestureRecognizer is UIScreenEdgePanGestureRecognizer)
    }
}

注意:颜色动画的协调由导航控制器完成。

{btsdaf} - Sti
{btsdaf} - clemens
我已经测试过了,它似乎没有覆盖使用交互式弹出手势向后滑动并改变主意并取消手势的情况 - 在这种情况下,它会设置视图控制器的色调颜色,如果您没有取消手势,则会将其弹出。 - Jasarien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接