web-dev-qa-db-fra.com

Animation de transition de commutateur RootViewController

Est-il possible d'avoir un effet Transition/animation en remplaçant un contrôleur de vue existant en tant que rootviewcontroller par un nouveau dans appDelegate?

120
Jefferson

Vous pouvez envelopper le changement de rootViewController dans un bloc d'animation de transition:

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

J'ai trouvé cela et fonctionne parfaitement:

dans votre appDéléguer:

- (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];
    }];
}

dans votre application

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

crédits:

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

65
Jesus

Je poste la réponse de Jésus mise en œuvre à Swift. Il prend l'identifiant de viewcontroller en tant qu'argument, se charge depuis le storyboard désiréViewController et modifie rootViewController avec animation.

Mise à jour 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();
    });
  }

Mise à jour 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;
  }

Après, vous avez un usage très simple de n'importe où:

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

Mise à jour Swift 3.0

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

// Dans Swift:

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

juste essayer ceci. Fonctionne bien pour moi.

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];
}];

MODIFIER:

Celui ci est mieux.

- (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);
        }
    }];
}
10
Dmitry Coolerov

Afin de ne pas avoir de problèmes avec la transition bascule plus tard dans l'application, il est bon d'effacer l'ancienne vue de la pile

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];
                }];
3
Catalin

Ceci est une mise à jour pour Swift 3, cette méthode doit figurer dans le délégué de votre application et vous l'appelez à partir de n'importe quel contrôleur de vue, via une instance partagée du délégué de l'application.

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)
}

La partie qui manque dans les diverses questions ci-dessus, est 

    self.window?.makeKeyAndVisible()

J'espère que ça aide quelqu'un.

2
Giovanny Piñeros

La bonne réponse est que vous n'avez pas besoin de remplacer la rootViewController sur votre fenêtre. Au lieu de cela, créez une UIViewController personnalisée, assignez-la une fois, laissez-la afficher un contrôleur enfant à la fois et remplacez-la par une animation si nécessaire. Vous pouvez utiliser le code suivant comme point de départ:

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
    }
}

Et la façon dont vous l'utilisez est:

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

L'exemple ci-dessus montre que vous pouvez imbriquer UINavigationController dans FrameViewController et que cela fonctionne parfaitement. Cette approche vous donne un haut niveau de personnalisation et de contrôle. Appelez simplement FrameViewController.display(_) à tout moment pour remplacer le contrôleur racine sur votre fenêtre. Ce travail sera fait à votre place.

2
Aleks N.

dans AppDelegate.h:

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

dans votre contrôleur:

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

Belle animation douce (testé avec 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)
    }
}

Appeler avec

guard let delegate = UIApplication.shared.delegate as? AppDelegate else { return }
delegate.present(viewController: UIViewController())
0
Cesare

Je propose mon chemin qui fonctionne bien dans mon projet, et il me propose de bonnes animations. J'ai testé d'autres propositions trouvées dans cet article, mais certaines d'entre elles ne fonctionnent pas comme prévu.

- (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
Saúl Moreno Abril