Réponses:
Vous pouvez encapsuler la commutation 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];
newViewController.view.layoutIfNeeded()
avant le bloc d'animation corrige les problèmes avec les éléments chargés paresseusement.
J'ai trouvé cela et fonctionne parfaitement:
dans votre applicationDelegate:
- (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:
Je publie la réponse de Jésus mise en œuvre rapidement. Il prend l'identifiant de viewcontroller comme argument, se charge à partir du storyboard désiréViewController et change rootViewController avec une 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 une utilisation 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")
Swift 2
UIView.transitionWithView(self.window!, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromLeft, animations: {
self.window?.rootViewController = anyViewController
}, completion: nil)
Swift 3, 4, 5
UIView.transition(with: self.window!, duration: 0.5, options: UIView.AnimationOptions.transitionFlipFromLeft, animations: {
self.window?.rootViewController = anyViewController
}, completion: nil)
essayez juste 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];
}];
ÉDITER:
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);
}
}];
}
UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionLayoutSubviews
, ou utilisez la première version ou une autre méthode.
Afin de ne pas avoir de problèmes avec le basculement de transition plus tard dans l'application, il est bon d'effacer également 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];
}];
La bonne réponse est que vous n'avez pas besoin de remplacer le rootViewController
sur votre fenêtre. Au lieu de cela, créez une personnalisation UIViewController
, attribuez-la une fois et laissez-la afficher un contrôleur enfant à la fois et remplacez-la par une animation si nécessaire. Vous pouvez utiliser le morceau de 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 à l' UINavigationController
intérieur FrameViewController
et que cela fonctionne très bien. Cette approche vous offre un niveau élevé de personnalisation et de contrôle. Appelez simplement à FrameViewController.display(_)
chaque fois que vous souhaitez remplacer le contrôleur racine de votre fenêtre, et il fera ce travail pour vous.
Ceci est une mise à jour pour swift 3, cette méthode doit être dans votre délégué d'application, et vous l'appelez depuis n'importe quel contrôleur de vue, via une instance partagée du délégué d'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 aux diverses questions ci-dessus est
self.window?.makeKeyAndVisible()
J'espère que cela aide quelqu'un.
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];
Je propose à ma façon que cela 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];
}
Belle animation douce (testée 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())