更改iOS窗口的根视图控制器
iOS窗口的根视图控制器是否通常在开始时被初始化为标签栏控制器或导航控制器? 可以在应用程序中多次更改根视图控制器吗?
我有一个场景,根据用户操作,顶视图是不同的。 我正在考虑使用带顶视图控制器的导航控制器,其中包含启animation面的图像,并根据需要推送/popup视图控制器。 或者,我可以不断改变窗口的顶视图控制器。 哪个会更好?
使用“呈现视图控制器”( presentViewController:animated:completion:
更为常见。 你可以有任意多的这些,只要你喜欢,有效地出现在(并基本上取代)根视图控制器。 如果你不想要,或者可以的话,不需要有任何animation。 您可以closures所提供的视图控制器以返回到原始的根视图控制器,但是您不必; 提出的视图控制器可以永远在那里,如果你喜欢。
以下是我书中介绍的视图控制器部分:
http://www.apeth.com/iOSBook/ch19.html#_presented_view_controller
在这个图中(从那一章开始),一个呈现的视图控制器已经完全接pipe了应用程序接口; 根视图控制器及其子视图不再处于界面中。 根视图控制器仍然存在,但这是轻量级的,并不重要。
iOS 8.0,Xcode 6.0.1,启用ARC
你的大部分问题都回答了。 但是,我可以解决一个我最近不得不面对的问题。
可以,在一个应用程序内多次更改根视图控制器吗?
答案是肯定的 。 我不得不这样做最近重置我的UIView层次结构的初始UIViews是应用程序的一部分。 开机不再需要。 换句话说,你可以在应用程序之后的任何时候从任何其他的UIViewController重置你的“rootViewController”。 “didFinishLoadingWithOptions”。
去做这个…
1)声明对您的应用程序的引用。 委托(应用程序称为“testing”)…
TestAppDelegate *testAppDelegate = (TestAppDelegate *)[UIApplication sharedApplication].delegate;
2)select一个你想使你的“rootViewController”的UIViewController; 从故事板或定义编程…
- a)storyboard(确保标识符,即storyboardID,存在于UIViewController的Identity Inspector中):
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)你甚至可以扔animation…
testAppDelegate.window.rootViewController = newRootViewController; [testAppDelegate.window makeKeyAndVisible]; newRootViewController.view.alpha = 0.0; [UIView animateWithDuration:2.0 animations:^{ newRootViewController.view.alpha = 1.0; }];
希望这可以帮助别人! 干杯。
窗口的根视图控制器。
根视图控制器提供窗口的内容视图。 将视图控制器分配给此属性(以编程方式或使用Interface Builder)将视图控制器的视图作为窗口的内容视图进行安装。 如果窗口具有现有的视图层次结构,则在安装新视图之前删除旧视图。 此属性的默认值为零。
*更新9/2/2015
正如下面的注释所指出的那样,当新的视图控制器出现时,你必须处理旧视图控制器的移除。 你可以select一个过渡视图控制器来处理这个。 以下是关于如何实现这个的一些提示:
[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; }];
从对serge-k的回答的评论,我已经build立了一个工作的解决scheme,有一个奇怪的行为的解决方法,当一个模式的视图控制器呈现在旧的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() } } }
要replacerootViewController,只需使用:
let newRootViewController = self.storyboard!.instantiateViewControllerWithIdentifier("BlackViewController") UIApplication.sharedApplication().keyWindow!.replaceRootViewControllerWith(newRootViewController, animated: true, completion: nil)
希望这有助于在iOS 8.4上testing; 还testing了导航控制器的支持(也应支持标签栏控制器等,但我没有testing)
说明
如果在旧的rootViewController上提供了一个模式视图控制器,则会取代rootViewController,但旧视图仍然挂在新的rootViewController视图的下方(例如在翻转水平或交叉溶解转换animation期间可以看到)和旧视图控制器层次结构仍然分配(如果replace发生多次,这可能导致严重的内存问题)。
所以唯一的解决办法是closures所有的模式视图控制器,然后replacerootViewController。 在解雇和更换期间,将屏幕快照放置在窗口上以隐藏丑陋的闪烁过程。
您可以在整个应用程序生命周期中更改窗口的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]; } } }
对于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; }]; }]; }