使用故事板编程设置初始视图控制器

267

我如何以编程的方式设置Storyboard中的InitialViewController?我想根据某些条件,在每次启动时打开Storyboard的不同视图。


1
请查看此答案,无需在设置中清除主故事板或警告。链接 - Borzh
25个回答

481

如何在没有虚拟初始视图控制器的情况下进行设置

确保所有初始视图控制器都具有Storyboard ID。

在Storyboard中,取消第一个视图控制器的“Is initial View Controller”属性的选中状态。

如果你此时运行你的应用程序,你会读到:

Failed to instantiate the default view controller for UIMainStoryboardFile 'MainStoryboard' - perhaps the designated entry point is not set?

你会注意到在应用程序代理中,窗口属性现在为nil。

在应用程序的设置中,转到目标和Info选项卡。清除Main storyboard file base name的值。在General选项卡中,清除Main Interface的值。这将删除警告。

在应用程序代理的application:didFinishLaunchingWithOptions:方法中创建窗口和所需的初始视图控制器:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];

    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];

    UIViewController *viewController = // determine the initial view controller here and instantiate it with [storyboard instantiateViewControllerWithIdentifier:<storyboard id>];

    self.window.rootViewController = viewController;
    [self.window makeKeyAndVisible];

    return YES;
}

7
爱!爱!爱!我将使用它在iOS6和iOS7之间切换两个完全不同的视图控制器树。看起来这是处理向后兼容性的最佳方式,同时仍然使用iOS7中所有的花里胡哨的功能。 - Hunkpapa
6
我正在学习使用Swift编写iOS程序。有人能帮助我用Swift编写上面的代码吗?请帮忙,谢谢。 - Raghavendra
1
@bdv didFinishLaunchingWithOptions 在应用程序在新进程中启动时被调用。如果您转到主屏幕并返回应用程序,则不会再次调用此方法。(除非iOS由于内存限制而终止。)尝试停止应用程序,然后再从IDE中启动一次。如果问题仍然存在,请将问题发布到SO上,我很乐意帮助您,朋友。 - Travis
2
根据我的调查,一旦删除对主故事板的引用,窗口就不存在了。我在当前项目中注释掉了窗口实例化,并发现情况仍然如此。@peyman - Travis
2
@Raghav,这是Swift代码:self.window = UIWindow(frame: UIScreen.mainScreen().bounds) var storyboard = UIStoryboard(name: "Main", bundle: nil) var viewController: UIViewController = // self.window!.rootViewController = viewController self.window!.makeKeyAndVisible() - mjmayank
显示剩余14条评论

128

对于所有的Swift爱好者,这里是SWIFT翻译版由@Travis提供的答案:

在Objective C代码之前执行@Travis之前解释的步骤,然后:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    var exampleViewController: ExampleViewController = mainStoryboard.instantiateViewControllerWithIdentifier("ExampleController") as! ExampleViewController

    self.window?.rootViewController = exampleViewController

    self.window?.makeKeyAndVisible()

    return true
}

ExampleViewController将成为您要显示的新的初始视图控制器。

操作步骤:

  1. 创建一个新窗口,大小与当前窗口相同,并将其设置为我们的主窗口
  2. 实例化一个storyboard,用于创建我们的新的初始视图控制器
  3. 根据Storyboard ID实例化我们的新初始视图控制器
  4. 将我们的新窗口的根视图控制器设置为我们刚刚初始化的新控制器
  5. 使我们的新窗口可见

祝您编程愉快!


我曾向@Travis询问过这个版本,但还是谢谢你。 - dellos
故事板ID位于身份检查器面板中的身份部分。 - Dominic
2
一个不需要故事板ID的技巧是在故事板中明确设置初始视图控制器(rootViewController),并使用实例方法UIStoryboard.instantiateInitialViewController()获取控制器,而不是上面提到的UIStoryboard.instantiateViewControllerWithIdentifier()。其余部分相同。 - Eric Boumendil

47

你可以在 (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法中以编程方式设置关键窗口的根视图控制器。

例如:

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if (shouldShowAnotherViewControllerAsRoot) {
        UIStoryboard *storyboard = self.window.rootViewController.storyboard;
        UIViewController *rootViewController = [storyboard instantiateViewControllerWithIdentifier:@"rootNavigationController"];
        self.window.rootViewController = rootViewController;
        [self.window makeKeyAndVisible];
    }

    return YES;
}

如何从次级UIViewController启动原始入口点? - ooxio
@ooxio:你可以将原始入口点存储在全局位置,然后稍后使用它。 - Chengjiong
如果您想要实例化登录/注册或类似的内容,这将非常有用... - mfaani
这比@Travis上面的答案简单得多,因为您不必去搞乱一百万个项目设置并在IB中进行调整。谁在乎你的一个视图控制器是否在技术上是默认的初始VC,然后您通过编程方式转到另一个VC? - Danny
请注意,替换的、故事板指定的初始视图控制器仍将被实例化并经历init()/deinit()周期,但不会执行viewDidLoad()或正确初始化IBOutlet。确保您的代码已准备好处理此情况。 - Gary
这是一种很好的方法,可以通过现有机制来有条件地维护对iOS 10.x用户的支持,同时为iOS 11的UIDocumentBrowserViewController添加支持。 - JKaz

21

SWIFT 5

如果您在Storyboard中没有设置ViewController作为初始ViewController,则需要执行以下两个操作:

  1. 转到项目的TARGETS,选择您的项目-> General->清除Main Interface字段。
  2. 仍在项目的TARGETS中,现在转到Info->应用程序场景清单->场景配置->应用程序会话角色->Item0(默认配置)->删除故事板名称字段。

最后,您现在可以在SceneDelegate中添加您的代码:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
    guard let windowScene = (scene as? UIWindowScene) else { return }

    window = UIWindow(windowScene: windowScene)


    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    // Make sure you set an Storyboard ID for the view controller you want to instantiate
    window?.rootViewController = storyboard.instantiateViewController(withIdentifier: identifier)
    window?.makeKeyAndVisible()

}

4
这绝对是截至Xcode 11.5的最新答案,确实解决了我在决定在代码中实例化初始VC后遇到的“无法实例化UIMainStoryboardFile 'Main'的默认视图控制器 - 可能未设置指定的入口点”警告。值得注意的是,当@rs7说“删除故事板名称字段”时,他们指的是plist的整个行,而不仅仅是该字段本身的内容。 - cdf1982
请注意,UIWindow(windowScene: windowScene)是与旧的基于App Delegate的代码中使用的initWithFrame:不同的UIWindow初始化程序。如果您正在移植到新的操作方式,请确保更新它。使用旧的窗口初始化程序会导致黑屏 - pkamb

15

Swift 3:更新@victor-sigler的代码

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    self.window = UIWindow(frame: UIScreen.main.bounds)

    // Assuming your storyboard is named "Main"
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)

    // Add code here (e.g. if/else) to determine which view controller class (chooseViewControllerA or chooseViewControllerB) and storyboard ID (chooseStoryboardA or chooseStoryboardB) to send the user to

    if(condition){
        let initialViewController: chooseViewControllerA = mainStoryboard.instantiateViewController(withIdentifier: "chooseStoryboardA") as! chooseViewControllerA
        self.window?.rootViewController = initialViewController
    )
    }else{
        let initialViewController: chooseViewControllerB = mainStoryboard.instantiateViewController(withIdentifier: "chooseStoryboardB") as! chooseViewControllerB
        self.window?.rootViewController = initialViewController
    )

    self.window?.makeKeyAndVisible(

    return true
}

谢谢你,@MEK。另外,你可能想通过用大括号替换尾随的括号来更正条件闭合语句的语法。 - Native_Mobile_Arch_Dev

10

您可以将导航根视图控制器设置为主视图控制器。这个想法可以用于根据应用程序要求进行自动登录。

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

UIViewController viewController = (HomeController*)[mainStoryboard instantiateViewControllerWithIdentifier: @"HomeController"];

UINavigationController navController = [[UINavigationController alloc] initWithRootViewController:viewController];

 self.window.rootViewController = navController;

if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) {

    // do stuff for iOS 7 and newer

    navController.navigationBar.barTintColor = [UIColor colorWithRed:88/255.0 green:164/255.0 blue:73/255.0 alpha:1.0];

    navController.navigationItem.leftBarButtonItem.tintColor = [UIColor colorWithRed:88/255.0 green:164/255.0 blue:73/255.0 alpha:1.0];

    navController.navigationBar.tintColor = [UIColor whiteColor];

    navController.navigationItem.titleView.tintColor = [UIColor whiteColor];

    NSDictionary *titleAttributes =@{

                                     NSFontAttributeName :[UIFont fontWithName:@"Helvetica-Bold" size:14.0],

                                     NSForegroundColorAttributeName : [UIColor whiteColor]

                                     };

    navController.navigationBar.titleTextAttributes = titleAttributes;

    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

}

else {

    // do stuff for older versions than iOS 7

    navController.navigationBar.tintColor = [UIColor colorWithRed:88/255.0 green:164/255.0 blue:73/255.0 alpha:1.0];



    navController.navigationItem.titleView.tintColor = [UIColor whiteColor];

}

[self.window makeKeyAndVisible];

对于StoryboardSegue用户

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

// Go to Login Screen of story board with Identifier name : LoginViewController_Identifier

LoginViewController *loginViewController = (LoginViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:@“LoginViewController_Identifier”];

navigationController = [[UINavigationController alloc] initWithRootViewController:testViewController];

self.window.rootViewController = navigationController;

[self.window makeKeyAndVisible];

// Go To Main screen if you are already Logged In Just check your saving credential here

if([SavedpreferenceForLogin] > 0){
    [loginViewController performSegueWithIdentifier:@"mainview_action" sender:nil];
}

谢谢


7

打开主storyboard,选择您想要首先启动的视图,然后打开"实用工具-->属性"。在"视图控制器"下面,您会看到"是初始视图控制器"单选按钮。只需选择它即可。

--- 对于修改后的问题:

也许您可以尝试这样做:在您的初始视图的ViewDidLoad部分编写一个方法,当应用程序启动时运行该方法,该方法将触发一个segue到另一个视图。


我把方法写在了ViewDidLoad中,但它没有起作用。当我把它写在viewdidAppear中时,它就可以工作了。你能解释一下为什么会这样吗? - Jagdev Sendhav
也许你可以尝试这样做:根据你的条件在appDelegate.m文件中的适当方法之一中添加一个segue代码。例如,你可以将segue代码添加到“applicationDidBecomeActive:”方法中。 - m.etka
@Jagdev 你应该在 ViewDidAppear 中编写它,而不是在 ViewDidLoad 中。 - Umair Khan Jadoon
这种方法的问题是初始视图控制器会在一段时间内变得可见,然后切换到其他视图控制器,从用户体验的角度来看并不好,因此不是一个好的解决方案。 - vishal dharankar

3

您可以通过Interface Builder以及编程的方式来设置初始视图控制器。

以下是编程方法:

Objective-C :


您可以使用Interface Builder以及编程的方式来设置initial view controller
        self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];

        UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"HomeViewController"]; // <storyboard id>

        self.window.rootViewController = viewController;
        [self.window makeKeyAndVisible];

        return YES;

Swift :

        self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)

        var objMainViewController: MainViewController = mainStoryboard.instantiateViewControllerWithIdentifier("MainController") as! MainViewController

        self.window?.rootViewController = objMainViewController

        self.window?.makeKeyAndVisible()

        return true

3

我认为这是不可能的。 相反,您可以有一个初始控制器,它将具有到不同视图控制器的转场。在启动时,您可以编程决定要执行哪个转场。


3

如果您在iOS 13+中使用场景代理(Scene Delegates):

请确保在您的Info.plist文件中找到以下行:

Application Scene Manifest > Scene Configuration > Application Session Role > Item 0

并且也需要在那里删除对于 Main Storyboard 的引用。否则,您将会收到有关无法从 storyboard 实例化的相同警告。

此外,将代码从应用程序委托移到场景委托方法 scene(_:willConnectTo:options:) 中,因为现在是处理生命周期事件的地方。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接