2

UINavigationBar背景色が異なるビュー間でスムーズなアニメーションを実現したいと考えています。埋め込みビューの背景色は同じで、次のUINavigationBarようなプッシュ/ポップ遷移アニメーションを模倣したい:

ここに画像の説明を入力

カスタムトランジションを用意しました:

class CustomTransition: NSObject, UIViewControllerAnimatedTransitioning {

    private let duration: TimeInterval
    private let isPresenting: Bool

    init(duration: TimeInterval = 1.0, isPresenting: Bool) {
        self.duration = duration
        self.isPresenting = isPresenting
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return duration
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let container = transitionContext.containerView
        guard
            let toVC = transitionContext.viewController(forKey: .to),
            let fromVC = transitionContext.viewController(forKey: .from),
            let toView = transitionContext.view(forKey: .to),
            let fromView = transitionContext.view(forKey: .from)
        else {
            return
        }

        let rightTranslation = CGAffineTransform(translationX: container.frame.width, y: 0)
        let leftTranslation = CGAffineTransform(translationX: -container.frame.width, y: 0)

        toView.transform = isPresenting ? rightTranslation : leftTranslation

        container.addSubview(toView)
        container.addSubview(fromView)

        fromVC.navigationController?.navigationBar.backgroundColor = .clear
        fromVC.navigationController?.navigationBar.setBackgroundImage(UIImage.fromColor(color: .clear), for: .default)

        UIView.animate(
            withDuration: self.duration,
            animations: {
                fromVC.view.transform = self.isPresenting ? leftTranslation :rightTranslation
                toVC.view.transform = .identity
            },
            completion: { _ in
                fromView.transform = .identity
                toVC.navigationController?.navigationBar.setBackgroundImage(
                    UIImage.fromColor(color: self.isPresenting ? .yellow : .lightGray),
                    for: .default
                )
                transitionContext.completeTransition(true)
            }
        )
    }
}

UINavigationControllerDelegateメソッドの実装でそれを返しました:

func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return CustomTransition(isPresenting: operation == .push)
}

プッシュ アニメーションはかなりうまく機能しますが、ポップは機能しません。

ここに画像の説明を入力

質問:

  1. ポップ アニメーションの前に NavBar の色をクリアした後、黄色のままになるのはなぜですか?
  2. 私の目標を達成するためのより良い方法はありますか?(ナビゲーションバーはフローの一部にすぎないため、常に透明にすることはできません)

これは、GitHub の私のテスト プロジェクトへのリンクです。

編集

これは、議論された問題と望ましい効果の全体像を示す gif です。

ここに画像の説明を入力

4

3 に答える 3

6

これらのコンポーネントは、常にカスタマイズが非常に困難です。Apple は、システム コンポーネントがすべてのアプリで同じように見えて動作することを望んでいると思います。これにより、iOS 環境全体で共有されたユーザー エクスペリエンスを維持できるからです。

システムのコンポーネントをカスタマイズするよりも、独自のコンポーネントを最初から実装する方が簡単な場合があります。コンポーネントが内部でどのように設計されているかがわからないため、カスタマイズが難しい場合があります。その結果、多くのエッジ ケースを処理し、不要な副作用に対処する必要があります。

それにもかかわらず、私はあなたの状況に対する解決策があると信じています. 私はあなたのプロジェクトをフォークし、あなたが説明した動作を実装しました。私の実装はGitHubにあります。animation-implementationブランチを参照してください。


UINavigationBar

ポップ アニメーションが適切に機能しない根本的な原因は、UINavigationBar独自の内部アニメーション ロジックを持っていることです。UINavigationController'sスタックが変更されると、変更するように指示UINavigationControllerUINavigationBarますUINavigationItems。そのため、最初に のシステム アニメーションを無効にする必要がありUINavigationItemsます。サブクラス化することで実行できますUINavigationBar

class CustomNavigationBar: UINavigationBar {
   override func pushItem(_ item: UINavigationItem, animated: Bool) {
     return super.pushItem(item, animated: false)
   }

   override func popItem(animated: Bool) -> UINavigationItem? {
     return super.popItem(animated: false)
   }
}

次に、次UINavigationControllerのように初期化する必要がありますCustomNavigationBar

let nc = UINavigationController(navigationBarClass: CustomNavigationBar.self, toolbarClass: nil)

UINavigationController


と の間でアニメーションをスムーズに同期させておく必要があるため、用のカスタム トランジション アニメーション オブジェクトを作成し、 と一緒に使用する必要がありUINavigationBarます。UIViewControllerUINavigationControllerCoreAnimationCATransaction

カスタムトランジション

トランジション アニメーターの実装はほぼ完璧ですが、私の観点から見れば、見落とされている詳細はほとんどありません。詳細については、遷移アニメーションのカスタマイズの記事を参照してください。UIViewControllerContextTransitioningまた、プロトコル内のメソッド コメントにも注意してください。

したがって、プッシュ アニメーションの私のバージョンは次のようになります。

func animatePush(_ transitionContext: UIViewControllerContextTransitioning) {
  let container = transitionContext.containerView

  guard let toVC = transitionContext.viewController(forKey: .to),
    let toView = transitionContext.view(forKey: .to) else {
      return
  }

  let toViewFinalFrame = transitionContext.finalFrame(for: toVC)
  toView.frame = toViewFinalFrame
  container.addSubview(toView)

  let viewTransition = CABasicAnimation(keyPath: "transform")
  viewTransition.duration = CFTimeInterval(self.duration)
  viewTransition.fromValue = CATransform3DTranslate(toView.layer.transform, container.layer.bounds.width, 0, 0)
  viewTransition.toValue = CATransform3DIdentity

  CATransaction.begin()
  CATransaction.setAnimationDuration(CFTimeInterval(self.duration))
  CATransaction.setCompletionBlock = {
      let cancelled = transitionContext.transitionWasCancelled
      if cancelled {
          toView.removeFromSuperview()
      }
      transitionContext.completeTransition(cancelled == false)
  }
  toView.layer.add(viewTransition, forKey: nil)
  CATransaction.commit()
}

ポップアニメーションの実装はほぼ同じです。とプロパティCABasicAnimationの値の唯一の違い。fromValuetoValue

UINavigationBar アニメーション

アニメーション化するには、レイヤーにアニメーションUINavigationBarを追加する必要があります。CATransitionUINavigationBar

let transition = CATransition()
transition.duration = CFTimeInterval(self.duration)
transition.type = kCATransitionPush
transition.subtype = self.isPresenting ? kCATransitionFromRight : kCATransitionFromLeft
toVC.navigationController?.navigationBar.layer.add(transition, forKey: nil)

上記のコードは全体をアニメーション化しますUINavigationBar。の背景のみをアニメーション化するには、UINavigationBarから背景ビューを取得する必要がありますUINavigationBar。ここに秘訣があります: ビューの最初のサブビューですUINavigationBar( _UIBarBackgroundXcode Debug View Hierarchy を使用して調べることができます)。この場合、正確なクラスは重要ではありませんUIView。最後に、アニメーション トランジションを_UIBarBackgroundのビュー レイヤーに直接追加できます。

let backgroundView = toVC.navigationController?.navigationBar.subviews[0]
backgroundView?.layer.add(transition, forKey: nil)

最初のサブビューがバックグラウンド ビューであると予測していることに注意してください。ビュー階層は将来変更される可能性がありますが、これだけは覚えておいてください。

CATransactionこの場合、これらのアニメーションは同時に実行されるため、両方のアニメーションを 1 つの に追加することが重要です。

すべてのView ControllerのメソッドでUINavigationBar背景色を設定できます。viewWillAppear

最終的なアニメーションは次のようになります。

ここに画像の説明を入力

これが役立つことを願っています。

于 2018-01-25T20:31:13.517 に答える