更改iOS窗口的根视图控制器

时间:2013-04-02 20:46:03

标签: ios rootviewcontroller

iOS窗口的根视图控制器通常在开头一次初始化为标签栏控制器或导航控制器吗?是否可以在应用程序中多次更改根视图控制器?

我有一个基于用户操作的顶视图不同的场景。我想到的是一个导航控制器,顶视图控制器具有启动画面的图像,并根据需要推/弹视图控制器。或者,我可以继续更改窗口的顶视图控制器。哪种方法更好?

6 个答案:

答案 0 :(得分:50)

iOS 8.0,Xcode 6.0.1,启用了ARC

您的大部分问题都已得到解答。但是,我可以解决一个我最近必须处理的问题。

可以在应用内多次更改根视图控制器吗?

答案是。最近我必须在最初的UIViews之后重置我的UIView层次结构,这是应用程序的一部分。不再需要启动。换句话说,您可以在应用程序之后随时从任何其他UIViewController重置“rootViewController”。 “didFinishLoadingWithOptions”。

要做到这一点......

1)声明对您的应用的引用。委托(app称为“测试”)......

TestAppDelegate *testAppDelegate = (TestAppDelegate *)[UIApplication sharedApplication].delegate;

2)选择一个你希望制作“rootViewController”的UIViewController;无论是从故事板还是以编程方式定义......

    a)故事板(确保标识符,即storyboardID,存在     在Identity Inspector中用于UIViewController):

UIStoryboard *mainStoryBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

NewRootViewController *newRootViewController = [mainStoryBoard instantiateViewControllerWithIdentifier:@"NewRootViewController"];

    b)以编程方式(可以添加子视图等)

UIViewController *newRootViewController = [[UIViewController alloc] init];
newRootViewController.view = [[UIView alloc] initWithFrame:CGRectMake(0, 50, 320, 430)];
newRootViewController.view.backgroundColor = [UIColor whiteColor];

3)把它们放在一起......

 testAppDelegate.window.rootViewController = newRootViewController;
[testAppDelegate.window makeKeyAndVisible];

4)你甚至可以投入动画......

testAppDelegate.window.rootViewController = newRootViewController;
    [testAppDelegate.window makeKeyAndVisible];

newRootViewController.view.alpha = 0.0;

    [UIView animateWithDuration:2.0 animations:^{

        newRootViewController.view.alpha = 1.0;

    }];

希望这有助于某人!欢呼声。

  

窗口的根视图控制器。

     

根视图控制器提供窗口的内容视图。   将视图控制器分配给此属性(以编程方式   或者使用Interface Builder)将视图控制器的视图安装为   窗口的内容视图。如果窗口具有现有视图   层次结构,旧视图在新视图之前被删除   安装。此属性的默认值为nil。

* 2015年9月2日更新

如下面的评论所指出的,您必须在显示新视图控制器时处理旧视图控制器的删除。您可以选择使用过渡视图控制器来处理此问题。以下是有关如何实现此目的的一些提示:

[UIView transitionWithView:self.containerView
                  duration:0.50
                   options:options
                animations:^{

                    //Transition of the two views
                    [self.viewController.view removeFromSuperview];
                    [self.containerView addSubview:aViewController.view];

                }
                completion:^(BOOL finished){

                    //At completion set the new view controller.
                    self.viewController = aViewController;

                }];

答案 1 :(得分:46)

更常见的是使用“呈现的视图控制器”(presentViewController:animated:completion:)。您可以根据需要拥有尽可能多的这些,有效地出现在(并且基本上替换)根视图控制器之前。如果你不想要,或者有可能,就没有任何动画。您可以关闭呈现的视图控制器以返回到原始根视图控制器,但您不必;如果您愿意,所呈现的视图控制器可以永远存在。

以下是我书中提供的视图控制器部分:

http://www.apeth.com/iOSBook/ch19.html#_presented_view_controller

在此图表中(从该章的前面部分开始),呈现的视图控制器已完全接管了应用程序界面;根视图控制器及其子视图不再在界面中。根视图控制器仍然存在,但这是轻量级的并不重要。

enter image description here

答案 2 :(得分:34)

从对serge-k的回答的评论中,当有一个模态视图控制器呈现在旧的rootViewController上时,我已经构建了一个工作解决方案,其中包含一种奇怪行为的解决方法:

extension UIView {
    func snapshot() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)
        drawViewHierarchyInRect(bounds, afterScreenUpdates: true)
        let result = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return result
    }
}

extension UIWindow {
    func replaceRootViewControllerWith(_ replacementController: UIViewController, animated: Bool, completion: (() -> Void)?) {
        let snapshotImageView = UIImageView(image: self.snapshot())
        self.addSubview(snapshotImageView)

        let dismissCompletion = { () -> Void in // dismiss all modal view controllers
            self.rootViewController = replacementController
            self.bringSubview(toFront: snapshotImageView)
            if animated {
                UIView.animate(withDuration: 0.4, animations: { () -> Void in
                    snapshotImageView.alpha = 0
                }, completion: { (success) -> Void in
                    snapshotImageView.removeFromSuperview()
                    completion?()
                })
            }
            else {
                snapshotImageView.removeFromSuperview()
                completion?()
            }
        }
        if self.rootViewController!.presentedViewController != nil {
            self.rootViewController!.dismiss(animated: false, completion: dismissCompletion)
        }
        else {
            dismissCompletion()
        }
    }
}

要替换rootViewController,只需使用:

let newRootViewController = self.storyboard!.instantiateViewControllerWithIdentifier("BlackViewController")
UIApplication.sharedApplication().keyWindow!.replaceRootViewControllerWith(newRootViewController, animated: true, completion: nil)

希望这会有所帮助:) 在iOS 8.4上测试;还测试了导航控制器支持(还应支持标签栏控制器等,但我没有测试它)

<强>解释

如果在旧的rootViewController上显示模态视图控制器,则替换rootViewController,但旧视图仍然悬挂在新的rootViewController视图下方(例如可以在Flip Horizo​​ntal或Cross Dissolve过渡期间看到)动画)和旧的视图控制器层次结构仍然被分配(如果多次更换,可能会导致严重的内存问题)。

所以唯一的解决方案是关闭所有模态视图控制器,然后替换rootViewController。在解雇和替换期间,屏幕快照被放置在窗口上,以隐藏丑陋的闪烁过程。

答案 3 :(得分:5)

您可以在整个应用程序生命周期中更改窗口的rootViewController。

UIViewController *viewController = [UIViewController alloc] init];
[self.window setRootViewController:viewController];

当您更改rootViewController时,您仍可能希望在窗口上添加UIImageView作为子视图以充当启动图像。我希望这是有道理的,像这样:

- (void) addSplash {
    CGRect rect = [UIScreen mainScreen].bounds;
    UIImageView *splashImage = [[UIImageView alloc] initWithFrame:rect];
    splashImage.image = [UIImage imageNamed:@"splash.png"];
    [self.window addSubview:splashImage];
}

- (void) removeSplash {
    for (UIView *view in self.window.subviews) {
      if ([view isKindOfClass:[UIImageView class]]) {
        [view removeFromSuperview];
      }
    }
}

答案 4 :(得分:3)

对于iOS8,我们还需要将以下两个参数设置为YES。

providesPresentationContextTransitionStyle
definesPresentationContext

这是我在iOS 6及更高版本的导航控制器下显示透明模型视图控制器的代码。

ViewController *vcObj = [[ViewController alloc] initWithNibName:NSStringFromClass([ViewController class]) bundle:nil];
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:vcObj];

if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {

    navCon.providesPresentationContextTransitionStyle = YES;
    navCon.definesPresentationContext = YES;
    navCon.modalPresentationStyle = UIModalPresentationOverCurrentContext;

    [self presentViewController:navCon animated:NO completion:nil];
}
else {

    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    [self presentViewController:navCon animated:NO completion:^{
        [navCon dismissViewControllerAnimated:NO completion:^{
            appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
            [self presentViewController:navCon animated:NO completion:nil];
            appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationFullScreen;

        }];
    }];
}

答案 5 :(得分:0)

对于尝试在 iOS 13及更高版本上更改根视图控制器的人,则需要使用SceneDelegate的{​​{1}}属性来更改根视图控制器。 / p>

window

创建了一个实用程序类,该类具有更改根视图控制器的方法。

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

  var window: UIWindow?
  static let shared = SceneDelegate()

  func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

    guard let _ = (scene as? UIWindowScene) else { return }

    //other stuff
  }
}

您可以通过以下方式更改根视图控制器。

class AppUtilities {

  class func changeRootVC( _ vc: UIViewController) {

    SceneDelegate.shared.window?.rootViewController = vc
    SceneDelegate.shared.window?.makeKeyAndVisible()
  }
}