在应用程序启动时从情节提要中选择替代的第一个视图控制器
Posted
技术标签:
【中文标题】在应用程序启动时从情节提要中选择替代的第一个视图控制器【英文标题】:selecting alternative first view controller from story board at application startup 【发布时间】:2012-06-05 07:42:39 【问题描述】:我刚刚开始使用 ios 编程,到目前为止,我在这里找到的教程和答案对我前进有很大帮助。然而,这个特殊的问题让我整晚都在烦恼,我找不到“感觉正确”的答案。
我正在编写一个连接到远程服务的应用程序,用户需要先登录才能使用它。当他们开始使用应用程序时,他们的第一个视图应该是登录对话框;当他们之前进行过身份验证时,他们会立即看到概览页面。
该项目使用故事板——我认为这是一个很棒的特性——所以选择和加载根视图控制器的大部分代码都已经处理好了。我认为添加逻辑的最佳位置是AppDelegate
的application:didFinishLaunchingWithOptions:
方法:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions
// select my root view controller here based on credentials present or not
return YES;
但这带来了两个问题:
在这个特定的委托方法中,根视图控制器已经根据故事板被选择(并加载?)。我是否可以在加载过程中移到更早的位置以覆盖第一个视图控制器选择,或者这会使事情变得不必要地复杂化?
要覆盖第一个视图控制器,我需要对故事板的引用,但我找不到比使用 UIStoryboard
的 storyboardWithName:bundle:
构造函数更好的方法。感觉不对,应用程序应该已经有故事板的引用,但是我怎么才能访问它呢?
更新
我解决了我遇到的第二个问题,因为我在这里找到了答案:
UIStoryboard: What's the Correct Way to Get the Active Storyboard?
NSBundle *bundle = [NSBundle mainBundle];
NSString *sbFile = [bundle objectForInfoDictionaryKey:@"UIMainStoryboardFile"];
UIStoryboard *sb = [UIStoryboard storyboardWithName:sbFile bundle:bundle];
以上将创建一个新的故事板实例;要获取活动实例,要简单得多:
UIStoryboard *sb = [[self.window rootViewController] storyboard];
在故事板文件本身中,您必须为要加载的视图设置标识符,例如LoginDialog
。然后你像这样实例化视图:
LoginViewController *login = [sb instantiateViewControllerWithIdentifier:@"LoginDialog"];
[self.window setRootViewController:login];
在另一个视图控制器中,以下就足够了:
UIStoryboard *sb = self.storyboard;
LoginViewController *login = [sb instantiateViewControllerWithIdentifier:@"LoginDialog"];
[self presentViewController:login animated:NO completion:nil];
【问题讨论】:
【参考方案1】:你可以只重置窗口的根视图控制器
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions
if(your_condition)
UIViewController *newRoot = [your implementation];
self.window.rootViewController = newRoot;
return YES;
这对我有用,Xcode5.0.1
【讨论】:
/* For storyboards... */ self.window.rootViewController = (YourViewController *)[[UIStoryboard storyboardWithName:@"Main" bundle: nil] instantiateViewControllerWithIdentifier:@"YourViewControllerID"];
【参考方案2】:
我的情况和你类似。我的应用程序使用UINavigationController
作为根视图控制器。如果用户已登录,我想向他/她显示NotLoggedInViewController
,而如果用户已登录,我想显示LoggedInViewController
。
在情节提要中,UINavigationController
只能有一个孩子,因此您必须能够以编程方式为其分配另一个根视图控制器。
我首先创建一个自定义导航控制器类,我们将其命名为MyNavigationController
。在情节提要中,我将此自定义类分配给导航控制器对象。
仍然在情节提要中,然后我对两个视图控制器进行建模,并将其中一个连接到导航控制器对象。由于稍后我需要能够从我的代码中访问它们,因此我使用右侧的 XCode 检查器为它们中的每一个分配了一个标识符。这些标识符可以是任意字符串,但为了简单起见,我只使用类名。
最后我在MyNavigationController
类上实现viewDidLoad
方法:
BOOL isLoggedIn = ...;
- (void)viewDidLoad
id rootController;
if (isLoggedIn)
rootController = [self.storyboard instantiateViewControllerWithIdentifier:@"LoggedInViewController"];
else
rootController = [self.storyboard instantiateViewControllerWithIdentifier:@"NotLoggedInViewController"];
self.viewControllers = [NSArray arrayWithObjects:rootController, nil];
【讨论】:
感谢您的回答;它证实了我找到的关于如何获取对活动故事板的引用并从中实例化视图的答案:) 我的决定是在应用程序委托中添加逻辑,主要是集中处理密码更改(远程服务确实不支持会话,并且在每次请求时都会发送散列密码)......仍在努力【参考方案3】:我几乎没有使用故事板,可能这不是您问题的确切答案。但我会建议你以我在项目中所做的工作,而无需使用情节提要。
在didFinishLaunchingWithOptions
AuthenticationViewController 是第一个加载的视图。它询问登录凭据。输入后,它将进入项目使用的实际 ViewControllers(即 TabBar &all..)。
添加到项目中的有趣功能是,当您输入凭据时,我会弹出一个UIAleretView
,要求用户选择三个选项之一。
-
无需密码即可保存凭据
使用密码保存凭据
不保存凭据
这里的密码只是用户输入的 4 位数字。每当他想“使用密码保存凭据”时,我 pushViewController
显示默认键盘的 NumberPad 和 popviewController
当它完成输入密码时。如果用户“不保存凭据”以及稍后在玩应用程序时想要使用其他身份验证选项,那么我将 TabBarController 的最后一个选项卡添加为“设置”选项卡,在其中我允许用户选择其中一个身份验证选项,弹出为 @ 987654326@在登录后启动应用的开头。
不要忘记将凭据保存在keychain
简而言之,
-
AuthenticationViewController-> 检查登录凭据是否存储在钥匙串中
1.1。如果未存储(即 3. 不保存凭据)-> 则显示登录页面。
1.2。如果凭据保存在钥匙串中-> 提取它们并查看它是否与密码绑定。
1.2.1。如果它与密码绑定(即 2. 使用密码保存凭据 )-> 然后显示密码页面。
1.2.2. 如果没有绑定(1. 保存没有密码的凭据)-> 然后显示/加载您项目的 TabBarController
层次结构或其他内容。这里实际上是您的应用程序开始。
【讨论】:
谢谢!我喜欢添加密码;目前我只是在钥匙链中写下帐户详细信息。当然,我可以让身份验证视图根据是否可以找到存储的凭据来执行视图更改,但我宁愿早点完成。 好的。当您使用故事板成功完成此操作时,我建议您将其作为示例项目发布在 github 或您的博客上。这将真正指导初学者很多。 当然!顺便说一句,找到了问题的第二部分:)【参考方案4】:主情节提要已经加载,只需找到它的引用,以便我可以实例化另一个根视图控制器:
UIStoryboard *mainStoryboard = self.window.rootViewController.storyboard;
self.window.rootViewController = [mainStoryboard
instantiateViewControllerWithIdentifier:@"view-controller-id"];
【讨论】:
它会爆炸的。 Segues 会被打破,你会从 UIKit 收到一些警告,天知道,也许在 iOS 9 中这会导致 CRASH。 @Andy 出于好奇,UIKit 的警告是什么? "警告:尝试在其视图不在窗口层次结构中的 XXX 上显示 XXX!"但我看到它在清理状态恢复缓存后消失了。无论如何,这种方法会破坏情节提要流程和状态恢复流程。 @Andy 够公平的;那么推荐的方法是什么?我的项目目前有一个根视图控制器,如果用户登录,它会执行对主应用程序的 segue ...这有效:) 我使用导航控制器,如果用户已经登录,只需推送下一个控制器。与 performSegue 方法不同,你可以在没有动画的情况下做到这一点。 Next 控制器隐藏了后退按钮,因此用户无法登录。您也可以在 root VC 的 viewDidAppear 中执行 performSegue,但这会暂时显示 root VC,这可能不是预期的效果。以上是关于在应用程序启动时从情节提要中选择替代的第一个视图控制器的主要内容,如果未能解决你的问题,请参考以下文章
在使用情节提要和 segue 时打开它之前选择 tabbarcontroller 的默认选项卡
如何在 swift UI 中单击按钮时从 swift UI 导航到情节提要?
如何使用情节提要为 ipad 的 xcode 中的第一个视图内的第二个视图添加动画
如何使用 swift 在情节提要中从一个视图控制器移动到另一个视图控制器 [重复]