web-dev-qa-db-ja.com

iOS10でのナビゲーションバーbarTintColorの変更のアニメーション化が機能しない

XCode 8.0/iOS 10にアップグレードしましたが、ナビゲーションバーの色変更アニメーションが機能しなくなり、アニメーションなしで直接色が変更されます。

UIView.animateWithDuration(0.2, animations: {
    self.navigationController?.navigationBar.barTintColor = currentSection.color!
})

誰もがこれを修正する方法を知っていますか?

11
Tiois

IOS10でnavigationBarの色の変化をアニメーション化するには、アニメーションブロック内の色を設定した後、layoutIfNeededを呼び出す必要があります。

コード例:

UIView.animateWithDuration(0.5) { 
    self.navigationController?.navigationBar.barTintColor = UIColor.redColor()
    self.navigationController?.navigationBar.layoutIfNeeded()
}

また、メソッドがいつでも壊れることがあるように、Apple 公式にはサポートされていません barTintColorなどのプロパティのアニメーションを通知したいと思います。

アニメーションブロック中にナビゲーションバーで-layoutIfNeededを呼び出すと、背景プロパティが更新されますが、これらのプロパティの機能の性質を考えると、これらのプロパティのいずれかをアニメーション化できるという保証はありません。

29
Vasily

インタラクティブアニメーション

Interactive animation

プロトコルを定義します。

/// Navigation bar colors for `ColorableNavigationController`, called on `Push` & `pop` actions
public protocol NavigationBarColorable: UIViewController {
    var navigationTintColor: UIColor? { get }
    var navigationBarTintColor: UIColor? { get }
}

public extension NavigationBarColorable {
    var navigationTintColor: UIColor? { return nil }
}

カスタムNavigationControllerサブクラスを定義します。

class AppNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationBar.shadowImage = UIImage()
        if let colors = rootViewController as? NavigationBarColorable {
            setNavigationBarColors(colors)            
        }
    }

    private var previousViewController: UIViewController? {
        guard viewControllers.count > 1 else {
            return nil
        }
        return viewControllers[viewControllers.count - 2]
    }

    override open func pushViewController(_ viewController: UIViewController, animated: Bool) {
        if let colors = viewController as? NavigationBarColorable {
            setNavigationBarColors(colors)
        }

        super.pushViewController(viewController, animated: animated)
    }

    override open func popViewController(animated: Bool) -> UIViewController? {
        if let colors = previousViewController as? NavigationBarColorable {
            setNavigationBarColors(colors)
        }

        // Let's start pop action or we can't get transitionCoordinator()
        let popViewController = super.popViewController(animated: animated)

        // Secure situation if user cancelled transition
        transitionCoordinator?.animate(alongsideTransition: nil, completion: { [weak self] context in
            guard let `self` = self else { return }

            guard let colors = self.topViewController as? NavigationBarColorable else { return }
            self.setNavigationBarColors(colors)
        })

        return popViewController
    }

    override func popToRootViewController(animated: Bool) -> [UIViewController]? {
        if let colors = rootViewController as? NavigationBarColorable {
            setNavigationBarColors(colors)
        }

        let controllers = super.popToRootViewController(animated: animated)

        return controllers
    }

    private func setNavigationBarColors(_ colors: NavigationBarColorable) {

        if let tintColor = colors.navigationTintColor {
            navigationBar.titleTextAttributes = [
                .foregroundColor : tintColor
            ]
            navigationBar.tintColor = tintColor
        }

        navigationBar.barTintColor = colors.navigationBarTintColor
    }
}

これで、NavigationBarColorable内の任意のコントローラーのAppNavigationControllerに準拠し、任意の色を指定できます。

extension FirstViewController: NavigationBarColorable {
    public var navigationBarTintColor: UIColor? { UIColor.red }
    public var navigationTintColor: UIColor? { UIColor.white }
}

extension SecondViewController: NavigationBarColorable {
    public var navigationBarTintColor: UIColor? { UIColor.blue }
    public var navigationTintColor: UIColor? { UIColor.orange }
}

この便利な拡張機能を実装することを忘れないでください:

extension UINavigationController {
    var rootViewController: UIViewController? {
        return viewControllers.first
    }
}
3