hachinoBlog

hachinobuのエンジニアライフ

遷移中にNavigationBarの設定をアニメーションさせて綺麗に見せる

背景

PushやModal遷移の時に呼び出し元ViewControllerと呼び出し先ViewControllerでNavigationBarやボタンの色が違う時に、その遷移の進捗に応じて設定の色などをアニメーションで変えて綺麗に見せたいかった

方法

UIViewControllerTransitionCoordinatorのメソッドである

func animate(alongsideTransition animation: ((UIViewControllerTransitionCoordinatorContext) -> Void)?, 
  completion: ((UIViewControllerTransitionCoordinatorContext) -> Void)? = nil) -> Bool

を使えば良い。

UIViewControllerTransitionCoordinatorはUIViewControllerのプロパティとして定義されている。

例えばNavigationBarの設定が違うMainViewControllerとDetailViewControllerあるとする

f:id:hachinobu:20180721212023p:plainf:id:hachinobu:20180721212034p:plain
遷移元MainViewControllerと遷移先DetailViewController

設定は呼び出し元のMainViewController.swifが

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.navigationController?.navigationBar.barTintColor = .blue
        self.navigationController?.navigationBar.tintColor = .white
    }

遷移先のDetailViewController.swif

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        transitionCoordinator?.animate(alongsideTransition: { _ in
            self.navigationController?.navigationBar.tintColor = .red
            self.navigationController?.navigationBar.barTintColor = .yellow
        }, completion: nil)
    }

といったようにやると、Pushなどの遷移中に遷移の比率に応じてアニメーションで色が変わっていくの良い。 シュミレーターの[Debug]-[Slow Animations]をONにして確かめると一目瞭然。

エッジスワイプバックのキャンセル対応

エッジスワイプで前の画面に戻ろうとしてキャンセルする操作をすると、ライフサイクル的に呼び出し元の画面のviewWillAppearが呼ばれ、現在表示されている遷移先画面はviewDidAppearが呼ばれるので、この実装だとエッジスワイプバックのキャンセルをすると、遷移元であるMainViewControllerの設定がDetailViewControllerに反映されてしまう。

f:id:hachinobu:20180721213919p:plainf:id:hachinobu:20180721213930p:plain
スワイプバックによる設定崩れ

なのでDetailViewControllerでtransitionCoordinator?.animateメソッドのcompletionブロックで、この画面の完成形となる色設定などを書いてあげておくと、違う画面の設定になってしまうということを防げる。

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        transitionCoordinator?.animate(alongsideTransition: { _ in
            self.navigationController?.navigationBar.tintColor = .red
            self.navigationController?.navigationBar.barTintColor = .yellow
        }, completion: { _ in
            self.navigationController?.navigationBar.tintColor = .red
            self.navigationController?.navigationBar.barTintColor = .yellow
        })
    }