设置UINavigationBar的barTintColor动画效果

6
我正在开发的应用程序在推送新视图控制器时更改其导航栏的barTintColor。目前,我们在目标视图控制器的viewWillAppear:方法中设置该颜色,但我们遇到了一些问题。
按照我们现在的方式,导航栏的颜色会突然改变,而其余的内容会像往常一样动画。我想要的是在源颜色和目标颜色之间淡入淡出的效果。有没有使用公共Cocoa Touch API来实现这个效果的方法?
3个回答

18
你可以使用UIViewControllerTransitionCoordinator添加额外的动画效果,使其与视图控制器过渡的时间和动画曲线相匹配。

在视图控制器的动画开始后(即在所呈现的视图控制器的viewWillAppear方法中),将会设置视图控制器的transitionCoordinator属性。使用转场协调器上的animateAlongsideTransition:completion:方法添加任何额外的动画效果。

示例:

[[self transitionCoordinator] animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
    self.navigationController.navigationBar.translucent = NO;
    self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
    self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
    self.navigationController.navigationBar.barTintColor = [UIColor redColor];
} completion:nil];

这在iOS 9和10上不起作用。它只是直接切换颜色。非常奇怪,考虑到它在prepareForSegue中可以工作。 - user2161301
5
在 iOS10 的推送操作中,这个方法对我有效,但是在弹出操作中不起作用(因为在弹出动画之后直接切换颜色)。 - charmingToad
@Happiehappie 是的,但我不得不以一种繁琐的方式来实现我想要的结果。发表在这里以防对其他人有用。https://dev59.com/33zaa4cB1Zd3GeqPMisY#42405212 - charmingToad

3
以下是更简单的修复方案。在执行viewWillDisappear时尝试设置导航栏外观将导致barTintColor不正确地进行动画处理。解决方法是在willMove(toParentViewController:)中设置它。下面的代码将产生平滑的渐变过渡效果,并且无论是手势还是按钮点击都可以,同时也适用于barStyle的动画处理。经测试,在iOS 10和11上都有效。
import UIKit

class RedViewController: UIViewController {

    override func viewWillAppear(_ animated: Bool) {
        self.title = "Red"
        self.navigationController?.navigationBar.barTintColor = .red
        self.navigationController?.navigationBar.tintColor = .white
    }

    override func willMove(toParentViewController parent: UIViewController?) {
        self.navigationController?.navigationBar.barTintColor = .white
        self.navigationController?.navigationBar.tintColor = nil
    }
}

这应该是正确的答案。简单而优雅! - Foti Dim

1
为了在推入和弹出时获得流畅的动画效果,我不得不使导航栏透明,并在其后面动画化自己的背景颜色视图。

这是我的UINavigationController子类处理:

import Foundation
import UIKit

class ColorTransitionNavigationController: UINavigationController {

    var navigationBarBackgroundView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Put a background view behind the navigation bar
        navigationBarBackgroundView = UIView()
        view.insertSubview(navigationBarBackgroundView, belowSubview: navigationBar)

        // Make the navigation bar transparent
        navigationBar.isTranslucent = true
        navigationBar.setBackgroundImage(UIImage(), for: .default)

        // Size the colored background to match the navigation bar
        navigationBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
        navigationBarBackgroundView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        navigationBarBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        navigationBarBackgroundView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

        // I used a hard-coded 64 instead of constraining to the height of the navigation bar because
        // when calling navigationController.setNavigationBarHidden(true), the height of the navigation bar becomes 0
        navigationBarBackgroundView.heightAnchor.constraint(equalToConstant: 64.0).isActive = true

    }

    func setBarTintColor(color: UIColor, animated: Bool, transitionCoordinator: UIViewControllerTransitionCoordinator?) {
        guard let transitionCoordinator = transitionCoordinator, animated else {
            navigationBarBackgroundView.backgroundColor = color
            return
        }

        transitionCoordinator.animateAlongsideTransition(in: view, animation: { [weak self] (context) in

            let transition = CATransition()
            transition.duration = context.transitionDuration
            transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
            self?.navigationBarBackgroundView.layer.add(transition, forKey: nil)
            self?.navigationBarBackgroundView.backgroundColor = color

        }, completion:nil)
    }
}

使用方法:

如果您希望一个UIViewController在出现时动画导航栏颜色,请覆盖viewWillAppear并调用setBarTintColor

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

        guard let navigationController = navigationController as? ColorTransitionNavigationController else { return }
        navigationController.setBarTintColor(color: UIColor.green, animated: animated, transitionCoordinator: transitionCoordinator)
    }

你能让标题颜色也生效吗? - ullstrm
很遗憾,我不记得了,我想我没有尝试在我的情况下更改标题颜色。 - charmingToad

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