RootViewControllerスイッチ遷移アニメーション


125

appDelegateでrootviewcontrollerとしての既存のビューコントローラーを新しいものに置き換えるときに、トランジション/アニメーション効果を持つ方法はありますか?

回答:


272

の切り替えをrootViewController遷移アニメーションブロックでラップできます。

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{ self.window.rootViewController = newViewController; }
                completion:nil];

5
ちょっとOle、私はこのアプローチを試しました、それは部分的に機能しました、それは私のアプリが横向きモードのままでいることですが、rootviewcontroller遷移を行うことによって、新しく提示されたビューコントローラは最初に縦向きでロードされ、すぐに横向きモードに回転します、それを解決する方法?
Chris Chen、

4
:私はここで彼の個別のご質問にクリス・チェンの質問(!?うまくいけば多分)に答えstackoverflow.com/questions/8053832/...
カレ

1
同じアニメーションでプッシュトランジションが必要なのですが、実現できますか?
ユーザー1531343 2014年

14
私はこれに関するいくつかの問題に気づきました、すなわち、誤って配置された要素/ゆるくロードされた要素です。たとえば、既存のルートvcにナビゲーションバーがない場合、ナビゲーションバーが1つある新しいvcにアニメーション化すると、アニメーションが完了し、ナビゲーションバーが追加されます。それはちょっと間抜けに見えます-これがなぜなのか、そして何ができるのかについての考えはありますか?
anon_dev1234 2014年

1
newViewController.view.layoutIfNeeded()アニメーションブロックの前に呼び出すと、遅延読み込みされた要素の問題が修正されることがわかりました。
おっと

66

私はこれを見つけて完全に機能しました:

あなたのappDelegateで:

- (void)changeRootViewController:(UIViewController*)viewController {

    if (!self.window.rootViewController) {
        self.window.rootViewController = viewController;
        return;
    }

    UIView *snapShot = [self.window snapshotViewAfterScreenUpdates:YES];

    [viewController.view addSubview:snapShot];

    self.window.rootViewController = viewController;

    [UIView animateWithDuration:0.5 animations:^{
        snapShot.layer.opacity = 0;
        snapShot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
    } completion:^(BOOL finished) {
        [snapShot removeFromSuperview];
    }];
}

あなたのアプリで

 if (!app) { app = (AppDelegate *)[[UIApplication sharedApplication] delegate]; }
        [app changeRootViewController:newViewController];

クレジット:

https://gist.github.com/gimenete/53704124583b5df3b407


これは自動画面回転をサポートしていますか?
Wingzero

1
私の場合、この解決策はよりうまくいきました。transitionWithViewを使用すると、新しいルートビューコントローラーは、移行が完了するまで適切にレイアウトされました。このアプローチでは、新しいルートビューコントローラーをウィンドウに追加し、レイアウトしてから遷移することができます。
フォスタ2017年

@Wingzeroは少し遅れますが、これにより、UIView.animations(たとえば、回転付きのCGAffineTransform)またはカスタムCAAnimationを介して、あらゆる種類の遷移が可能になります。

41

迅速に実装されたイエスの答えを投稿しています。これは、viewcontrollerの識別子を引数として取り、ストーリーボードのdesiredViewControllerから読み込み、rootViewControllerをアニメーションで変更します。

Swift 3.0アップデート:

  func changeRootViewController(with identifier:String!) {
    let storyboard = self.window?.rootViewController?.storyboard
    let desiredViewController = storyboard?.instantiateViewController(withIdentifier: identifier);

    let snapshot:UIView = (self.window?.snapshotView(afterScreenUpdates: true))!
    desiredViewController?.view.addSubview(snapshot);

    self.window?.rootViewController = desiredViewController;

    UIView.animate(withDuration: 0.3, animations: {() in
      snapshot.layer.opacity = 0;
      snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
      }, completion: {
        (value: Bool) in
        snapshot.removeFromSuperview();
    });
  }

Swift 2.2アップデート:

  func changeRootViewControllerWithIdentifier(identifier:String!) {
    let storyboard = self.window?.rootViewController?.storyboard
    let desiredViewController = storyboard?.instantiateViewControllerWithIdentifier(identifier);

    let snapshot:UIView = (self.window?.snapshotViewAfterScreenUpdates(true))!
    desiredViewController?.view.addSubview(snapshot);

    self.window?.rootViewController = desiredViewController;

    UIView.animateWithDuration(0.3, animations: {() in
      snapshot.layer.opacity = 0;
      snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
      }, completion: {
        (value: Bool) in
        snapshot.removeFromSuperview();
    });
  }

  class func sharedAppDelegate() -> AppDelegate? {
    return UIApplication.sharedApplication().delegate as? AppDelegate;
  }

その後、どこからでも非常に簡単な使い方ができます。

let appDelegate = AppDelegate.sharedAppDelegate()
appDelegate?.changeRootViewControllerWithIdentifier("YourViewControllerID")

Swift 3.0アップデート

let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.changeRootViewController(with: "listenViewController")

25

スウィフト2

UIView.transitionWithView(self.window!, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromLeft, animations: {
  self.window?.rootViewController = anyViewController
}, completion: nil)

スイフト3、4、5

UIView.transition(with: self.window!, duration: 0.5, options: UIView.AnimationOptions.transitionFlipFromLeft, animations: {
  self.window?.rootViewController = anyViewController
}, completion: nil)

XCodeは次のように私のコードを修正しました: `` `UIView.transition(with:self.view.window !, duration:0.5、options:UIViewAnimationOptions.transitionFlipFromTop、animations:{appDelegate.window?.rootViewController = myViewController}、completion:nil) `` `
scaryguy

10

これを試してみてください。私にとってはうまくいきます。

BOOL oldState = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
self.window.rootViewController = viewController;
[UIView transitionWithView:self.window duration:0.5 options:transition animations:^{
    //
} completion:^(BOOL finished) {
    [UIView setAnimationsEnabled:oldState];
}];

編集:

これは良いです。

- (void)setRootViewController:(UIViewController *)viewController
               withTransition:(UIViewAnimationOptions)transition
                   completion:(void (^)(BOOL finished))completion {
    UIViewController *oldViewController = self.window.rootViewController;
    [UIView transitionFromView:oldViewController.view 
                        toView:viewController.view
                      duration:0.5f
                       options:(UIViewAnimationOptions)(transition|UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionLayoutSubviews)
                    completion:^(BOOL finished) {
        self.window.rootViewController = viewController;
        if (completion) {
            completion(finished);
        }
    }];
}

ルートVCを切り替えるだけで、奇妙なデフォルトアニメーションが表示されました。最初のバージョンは私のためにそれを取り除きました。
juhan_h 2016

2番目のバージョンは、juhan_hで言及されているように、サブビューのレイアウトをアニメーション化します。これが不要な場合は、を試してみるかUIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionLayoutSubviews、最初のバージョンを使用するか、他の方法を使用してください。
ftvs 2016年

3

アプリで後でトランジションフリップの問題が発生しないようにするために、スタックから古いビューもクリアすることをお勧めします

UIViewController *oldController=self.window.rootViewController;

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{ self.window.rootViewController = nav; }
                completion:^(BOOL finished) {
                    if(oldController!=nil)
                        [oldController.view removeFromSuperview];
                }];

2

正解はrootViewController、ウィンドウのを交換する必要がないことです。代わりに、カスタムを作成しますUIViewController一度割り当てて、一度に1つの子コントローラーを表示させ、必要に応じてアニメーションに置き換えます。以下のコードを開始点として使用できます。

Swift 3.0

import Foundation
import UIKit

/// Displays a single child controller at a time.
/// Replaces the current child controller optionally with animation.
class FrameViewController: UIViewController {

    private(set) var displayedViewController: UIViewController?

    func display(_ viewController: UIViewController, animated: Bool = false) {

        addChildViewController(viewController)

        let oldViewController = displayedViewController

        view.addSubview(viewController.view)
        viewController.view.layoutIfNeeded()

        let finishDisplay: (Bool) -> Void = {
            [weak self] finished in
            if !finished { return }
            oldViewController?.view.removeFromSuperview()
            oldViewController?.removeFromParentViewController()
            viewController.didMove(toParentViewController: self)
        }

        if (animated) {
            viewController.view.alpha = 0
            UIView.animate(
                withDuration: 0.5,
                animations: { viewController.view.alpha = 1; oldViewController?.view.alpha = 0 },
                completion: finishDisplay
            )
        }
        else {
            finishDisplay(true)
        }

        displayedViewController = viewController
    }

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return displayedViewController?.preferredStatusBarStyle ?? .default
    }
}

そしてそれを使用する方法は次のとおりです:

...
let rootController = FrameViewController()
rootController.display(UINavigationController(rootViewController: MyController()))
window.rootViewController = rootController
window.makeKeyAndVisible()
...

上記の例は、UINavigationController内部にネストすることができFrameViewController、問題なく機能することを示しています。このアプローチにより、高度なカスタマイズと制御が可能になります。FrameViewController.display(_)ウィンドウのルートコントローラを交換したいときにいつでも電話をかけるだけで、その作業が自動的に行われます。


2

これはSwift 3の更新です。このメソッドはアプリデリゲートにある必要があり、アプリデリゲートの共有インスタンスを介して、任意のビューコントローラーから呼び出します

func logOutAnimation() {
    let storyBoard = UIStoryboard.init(name: "SignIn", bundle: nil)
    let viewController = storyBoard.instantiateViewController(withIdentifier: "signInVC")
    UIView.transition(with: self.window!, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromLeft, animations: {
        self.window?.rootViewController = viewController
        self.window?.makeKeyAndVisible()
    }, completion: nil)
}

上記のさまざまな質問から欠落している部分は、

    self.window?.makeKeyAndVisible()

これが誰かを助けることを願っています。


1

AppDelegate.h:

#define ApplicationDelegate ((AppDelegate *)[UIApplication sharedApplication].delegate)]

あなたのコントローラーで:

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{
    ApplicationDelegate.window.rootViewController = newViewController;
    }
                completion:nil];

6
これは、フォーマットが間違っていることを除いて、受け入れられた回答と同じです。なぜわざわざ?
jrturton 2013年

1
これは、ViewまたはViewControllerにいることに依存しません。最大の違いは、ViewとViewControllerの厚さまたは薄さの点で、より哲学的です。
最大

0

私のプロジェクトでうまく機能する方法を提案し、それは私に良いアニメーションを提供します。この投稿で見つかった他の提案をテストしましたが、一部は期待どおりに機能しません。

- (void)transitionToViewController:(UIViewController *)viewController withTransition:(UIViewAnimationOptions)transition completion:(void (^)(BOOL finished))completion {
// Reset new RootViewController to be sure that it have not presented any controllers
[viewController dismissViewControllerAnimated:NO completion:nil];

[UIView transitionWithView:self.window
                  duration:0.5f
                   options:transition
                animations:^{
                    for (UIView *view in self.window.subviews) {
                        [view removeFromSuperview];
                    }
                    [self.window addSubview:viewController.view];

                    self.window.rootViewController = viewController;
                } completion:completion];
}

0

素敵な甘いアニメーション(Swift 4.xでテスト済み):

extension AppDelegate {
   public func present(viewController: UIViewController) {
        guard let window = window else { return }
        UIView.transition(with: window, duration: 0.5, options: .transitionFlipFromLeft, animations: {
            window.rootViewController = viewController
        }, completion: nil)
    }
}

と電話する

guard let delegate = UIApplication.shared.delegate as? AppDelegate else { return }
delegate.present(viewController: UIViewController())
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.