用animation交换rootViewController

我有点麻烦交换与animation的rootViewControllers。 以下是我正在使用的代码:

[UIView transitionWithView:self.window duration:0.8 options:UIViewAnimationOptionTransitionFlipFromRight animations:^{ self.window.rootViewController = self.navigationController; } completion:nil]; 

除了在animation之前的一些作品之外,屏幕变成黑色,然后animation出现。 它看起来像原始的rootViewController正在animation之前被删除。 有任何想法吗?

transitionWithView旨在为指定的容器视图的子视图设置animation。 animation改变根视图控制器并不那么简单。 我花了很长时间试图做它没有副作用。 看到:

在不使用导航控制器堆栈,子视图或模态控制器的情况下animation化视图控制器的更改?

编辑:添加引用的答案摘录

 [UIView transitionFromView:self.window.rootViewController.view toView:viewController.view duration:0.65f options:transition completion:^(BOOL finished){ self.window.rootViewController = viewController; }]; 

我知道这是一个相当古老的问题,但是,接受的答案和替代scheme都没有提供一个好的解决scheme(当animation完成新的根视图控制器的导航栏从白色闪烁到背景颜色,这是非常震惊) 。

我通过快照第一个视图控制器的最后一个屏幕,将其覆盖在第二个(目的地)视图控制器上,然后将其设置为根的第二个视图控制器之后我想要的animation:

 UIView *overlayView = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO]; [self.destinationViewController.view addSubview:overlayView]; self.window.rootViewController = self.destinationViewController; [UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ overlayView.alpha = 0; } completion:^(BOOL finished) { [overlayView removeFromSuperview]; }]; 

编辑: Swift 3版本的代码:

 let overlayView = UIScreen.main.snapshotView(afterScreenUpdates: false) destinationViewController.view.addSubview(overlayView) window.rootViewController = destinationViewController UIView.animate(withDuration: 0.4, delay: 0, options: .transitionCrossDissolve, animations: { overlayView.alpha = 0 }, completion: { finished in overlayView.removeFromSuperview() }) 

我已经find了transitionWithView:duration:options:animations:completion:来产生更可靠的结果。

 [UIView transitionWithView:window duration:0.3 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{ [fromView removeFromSuperview]; [window addSubview:toView]; window.rootViewController = toViewController; } completion:NULL]; 

如果您将其保留至完成块以设置根视图控制器,则在诸如视图(将/出现)之类的方法出现时

 self.view.window.rootViewController 

仍然会被设置为前一个视图控制器。 在大多数情况下,这可能不是问题,除非您需要像那样在那些方法期间将对rootViewController的引用传递给其他代码。

对我而言,解决scheme只是对Marko Nikolovski的回答稍作修改。 我现有的根视图控制器上有一个animation微调,所以快照看起来很奇怪,因为它冻结了animation。 最后,我能够做到以下(在当前的根视图控制器内):

 NextRootViewController *nextRootVC = [self.storyboard instantiateViewControllerWithIdentifier:@"NextRootViewController"]; self.view.window.rootViewController = nextRootVC; [nextRootVC addSubview:self.view]; [UIView animateWithDuration:0.4 delay:0.2 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ self.view.alpha = 0; } completion:^(BOOL finished) { [self.view removeFromSuperview]; }]; 

目前接受的答案(写这个答案的时候)是不理想的,因为它可能不会在animation期间正确定位导航栏等UI元素,或导致Marko Nikolovski的回答中所述的其他布局故障 。 然而,将当前视图控制器的视图的快照推到下一个视图控制器视图的顶部用于animation目的是不理想的,因为它不能用于视图控制器,其中在转换到下一个视图的过程中发生任何types的animation控制器作为一个快照只是一个静态的图像

考虑到cclogg的基本思想 ,我的实现是在两个视图控制器之间平滑过渡,而不用担心alpha值:

 /// Replaces the window's current `rootViewController` with the `newViewController` /// passed as a parameter. If `animated`, the window animates the replacement /// with a cross dissolve transition. /// /// - Parameters: /// - newViewController: The view controller that replaces the current view controller. /// - animated: Specifies if the transition between the two view controllers is animated. /// - duration: The transition's duration. (Ignored if `animated == false`) private func swapCurrentViewController(with newViewController: UIViewController, animated: Bool = true, duration: TimeInterval = 1) { // Get a reference to the window's current `rootViewController` (the "old" one) let oldViewController = window?.rootViewController // Replace the window's `rootViewController` with the new one window?.rootViewController = newViewController if animated, let oldView = oldViewController?.view { // Add the old view controller's view on top of the new `rootViewController` newViewController.view.addSubview(oldView) // Remove the old view controller's view in an animated fashion UIView.transition(with: window!, duration: duration, options: .transitionCrossDissolve, animations: { oldView.removeFromSuperview() }, completion: nil) } } 

启动转换或animation之前 ,replace窗口的rootViewController非常重要,因为这就是新的视图控制器如何知道其上下文,即正确的布局边距等。


💡如果您确实需要replace窗口的rootViewController出于任何原因,这是一种方法。 不过,我想指出的是,从架构的angular度来看,我认为更好的方法是创build一个视图控制器,负责处理其他两个视图控制器之间的转换,并将其设置为窗口的rootViewController ,然后再从未改变它 。 这个答案很好地解释了一个类似的问题。

为什么不交换rootViewControllers,为什么不创build一个自定义的视图,或者可能只是一个UINavigationController(不显示标题栏),并把你想交换的“rootViews”之下。