通过情节提要以编程方式设置初始视图控制器


Answers:


466

如何使用虚拟初始视图控制器

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

在情节提要中,取消选中第一个视图控制器中的“是初始视图控制器”属性。

如果您此时运行应用程序,则将阅读:

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

您会注意到,应用程序委托中的window属性现在为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 2013年

6
我正在Swift中学习iOS编程。谁能帮助我迅速编写上述代码。请帮忙。谢谢。
Raghavendra 2014年

1
didFinishLaunchingWithOptions在新进程中启动应用程序时,将调用@bdv 。如果转到主屏幕并返回到应用程序,则不会再次调用此方法。(除非iOS由于内存限制而终止。)请尝试停止该应用程序,然后从您的IDE重新启动。如果问题仍然存在,请将问题发布到SO,我很乐意为您提供帮助,朋友。
特拉维斯2014年

2
@peyman根据我的调查,一旦删除对主故事板的引用,该窗口将不存在。我注释了当前项目中的窗口实例化,发现情况仍然如此。
特拉维斯2015年

2
@Raghav,这是快速代码: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

121

对于所有Swift爱好者,以下是答案 @Travis译为SWIFT

做什么 @Travis在目标C代码之前说明的内容。然后,

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. 实例化一个情节提要,我们可以用来创建新的初始视图控制器
  3. 根据情节提要ID实例化新的初始视图控制器
  4. 将新窗口的根视图控制器设置为刚启动的新控制器
  5. 使我们的新窗口可见

享受快乐的编程!


我曾问过@Travis这个版本,然后……。不管怎么说,还是要谢谢你。
dellos

故事图板ID在“身份”检查器窗格中的“身份”部分下
Dominic

2
不需要情节提要ID的技巧是在情节提要中显式设置初始视图控制器(rootViewController),并使用实例方法UIStoryboard.instantiateInitialViewController()来获取控制器,而不是上面提出的UIStoryboard.instantiateViewControllerWithIdentifier()。其余部分相同。
埃里克·布孟迪尔

45

您可以以编程方式在其中设置键窗口的rootViewController (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:您可以将原始入口点存储在全局位置,然后在以后使用。
炯2015年

如果您想实例化登录/注册或类似的东西,这真的很有用...
Honey

这比@Travis的答案要简单得多,因为您不必费心处理一百万个项目设置并在IB中进行修改。谁在乎您的视图控制器在技术上是否是默认的初始VC,然后以编程方式转移到其他视图控制器?
丹尼

请注意,替换的,由情节提要指定的初始View Controller仍将被实例化并执行init()/ deinit()循环,但不会执行viewDidLoad()或未正确初始化IBOutlet-s。确保您已编写代码。
加里

14

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 '19

9

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

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];

对于情节提要Segue用户

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];
}

谢谢


6

打开主板,选择要首先启动的视图,然后打开实用程序->属性。在“视图控制器”下方,您会看到“是初始视图控制器”单选按钮。只需选择它。

---对修改后的问题:

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


我在ViewDidLoad中编写了该方法,但是它不起作用,当我在viewdidAppear中将其选中时,它可以工作,您能解释一下为什么会发生这种情况。
UserDev 2012年

可能是您应该尝试执行以下操作:根据您的条件将segue代码添加到appDelegate.m文件中的相应方法之一。例如,您可以将segue代码添加到“ applicationDidBecomeActive:”方法。
m.etka 2012年

@Jagdev是在ViewDidAppear中而不是ViewDidLoad中编写的。
Umair Khan Jadoon

这种方法的问题是,最初的视图控制器在一段时间内变得可见,然后切换到其他视图,这从UX的角度来看不是很好,因此不是一个好的解决方案。
vishal dharankar 2014年

3

SWIFT 5

如果您没有将Storyboard中的ViewController设置为ViewController,则需要做两件事:

  1. 转到项目TARGETS,选择项目->常规->清除主界面字段。
  2. 始终位于项目TARGETS中,现在转到信息->应用程序场景清单->场景配置->应用程序会话角色-> 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()

}

这肯定是Xcode 11.5以来最新的答案,并且确实修复了Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set我决定在代码中实例化初始VC后收到的警告。重要的一点是,当@ rs7说“删除情节提要名称字段”时,它们表示plist的整个行,而不仅仅是字段本身的内容。
cdf1982

2

我认为不可能。取而代之的是,您可以拥有一个初始控制器,该控制器将与不同的视图控制器进行交互。在启动时,您可以决定以编程方式执行哪个序列。


2

您可以initial view controller使用Interface Builder以及以编程方式进行设置。

以下是用于编程的方法。

目标-C:

        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;

斯威夫特:

        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

2

我创建了一个路由类来处理动态导航并保持干净的AppDelegate类,希望对其他人也有帮助。

//
//  Routing.swift
// 
//
//  Created by Varun Naharia on 02/02/17.
//  Copyright © 2017 TechNaharia. All rights reserved.
//

import Foundation
import UIKit
import CoreLocation

class Routing {

    class func decideInitialViewController(window:UIWindow){
        let userDefaults = UserDefaults.standard
        if((Routing.getUserDefault("isFirstRun")) == nil)
        {
            Routing.setAnimatedAsInitialViewContoller(window: window)
        }
        else if((userDefaults.object(forKey: "User")) != nil)
        {
            Routing.setHomeAsInitialViewContoller(window: window)
        }
        else
        {
            Routing.setLoginAsInitialViewContoller(window: window)
        }

    }

    class func setAnimatedAsInitialViewContoller(window:UIWindow) {
        Routing.setUserDefault("Yes", KeyToSave: "isFirstRun")
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let animatedViewController: AnimatedViewController = mainStoryboard.instantiateViewController(withIdentifier: "AnimatedViewController") as! AnimatedViewController

        window.rootViewController = animatedViewController
        window.makeKeyAndVisible()
    }

    class func setHomeAsInitialViewContoller(window:UIWindow) {
        let userDefaults = UserDefaults.standard
        let decoded  = userDefaults.object(forKey: "User") as! Data
        User.currentUser = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! User

        if(User.currentUser.userId != nil && User.currentUser.userId != "")
        {
            let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
            let homeViewController: HomeViewController = mainStoryboard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
            let loginViewController: UINavigationController = mainStoryboard.instantiateViewController(withIdentifier: "LoginNavigationViewController") as! UINavigationController
            loginViewController.viewControllers.append(homeViewController)
            window.rootViewController = loginViewController
        }
        window.makeKeyAndVisible()
    }

    class func setLoginAsInitialViewContoller(window:UIWindow) {
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let loginViewController: UINavigationController = mainStoryboard.instantiateViewController(withIdentifier: "LoginNavigationViewController") as! UINavigationController

        window.rootViewController = loginViewController
        window.makeKeyAndVisible()
    }

  class func setUserDefault(_ ObjectToSave : Any?  , KeyToSave : String)
    {
        let defaults = UserDefaults.standard

        if (ObjectToSave != nil)
        {

            defaults.set(ObjectToSave, forKey: KeyToSave)
        }

        UserDefaults.standard.synchronize()
    }

    class func getUserDefault(_ KeyToReturnValye : String) -> Any?
    {
        let defaults = UserDefaults.standard

        if let name = defaults.value(forKey: KeyToReturnValye)
        {
            return name as Any
        }
        return nil
    }

    class func removetUserDefault(_ KeyToRemove : String)
    {
        let defaults = UserDefaults.standard
        defaults.removeObject(forKey: KeyToRemove)
        UserDefaults.standard.synchronize()
    }

}

然后在您的AppDelegate中调用此

 self.window = UIWindow(frame: UIScreen.main.bounds)
 Routing.decideInitialViewController(window: self.window!)

2

使用Swift 3Swift 4避免强制转换的另一种解决方案是这样的

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    self.window = UIWindow(frame: UIScreen.main.bounds)
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    guard let viewController = storyboard.instantiateViewController(withIdentifier: "YourViewController") as? YourViewController else {
        return false
    }
    self.window?.rootViewController = viewController
    self.window?.makeKeyAndVisible()
    return true
}

和下面一起使用 UINavigationController

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    self.window = UIWindow(frame: UIScreen.main.bounds)
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    guard let viewController = storyboard.instantiateViewController(withIdentifier: "YourViewController") as? YourViewController else {
        return false
    }
    let navigationController = UINavigationController(rootViewController: viewController)
    self.window?.rootViewController = navigationController
    self.window?.makeKeyAndVisible()
    return true
}

2

在其中AppDelegate.swift可以添加以下代码:

let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "YourViewController_StorboardID")
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()

当然,您需要根据选择合适的视图控制器的标准来实现逻辑。

另外,不要忘记添加身份(选择情节提要->控制器场景->显示身份检查器->分配StorboardID)。


1

适用于iOS 13和场景委托的更新的答案:

确保在info.plist文件中,进入“应用程序场景清单”->“场景配置”->“应用程序会话角色”->“项目0”,并在那里删除对主故事板的引用。否则,您将收到有关无法从情节提要实例化的相同警告。

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


0

几天前,我遇到了同样的情况。一个非常简单的技巧解决了这个问题。我在启动2之前将隐藏的初始视图控制器设置为隐藏。如果初始视图控制器是正确的控制器,则将其设置为在viewDidLoad中可见。否则,对期望的视图控制器执行segue。它在iOS 6.1及更高版本中完美运行。我确定它可以在iOS的早期版本上使用。


0

感谢在AppDelegate中如下修改:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) ->     Bool {
//Some code to check value of pins

if pins! == "Verified"{
        print(pins)
        self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "HomePage", bundle: nil)
        let exampleViewController: UINavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("SBHP") as! UINavigationController

        self.window?.rootViewController = exampleViewController

        self.window?.makeKeyAndVisible()
    }else{
        print(pins)
        self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let exampleViewController: UINavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("SBUser") as! UINavigationController

        self.window?.rootViewController = exampleViewController

        self.window?.makeKeyAndVisible()
    }

0

找到简单的解决方案-无需从情节提要中删除“初始视图控制器检查”并编辑项目的“信息”选项卡并使用makeKeyAndVisible,只需放置

self.window.rootViewController = rootVC;

- (BOOL) application:didFinishLaunchingWithOptions:

但你还是得rootVCinstantiateViewControllerWithIdentifier,正确吗?
thomers

0
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "storyBoardid") as! ViewController
let navigationController = UINavigationController(rootViewController: vc)
UIApplication.shared.delegate.window?.rootViewController = navigationController

另一种方法是呈现viewController,

let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "storyBoardid") as! ViewController
self.present(vc,animated:true,completion:nil)

首先,您需要创建情节提要的对象,然后更改根目录(如果需要),然后引用被推入当前视图控制器的特定视图控制器(如果您更改根目录),否则它只是呈现新的视图控制器,您可能


@VD Purohit,您能否详细描述您的答案以获得更多理解。
Prags

0

斯威夫特4,Xcode 9

在文件AppDelegate.swift中

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let firstVC = storyboard.instantiateViewController(withIdentifier: "firstViewController") as! firstViewController
    self.window?.rootViewController = firstVC
}

0
 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        self.window = UIWindow(frame: UIScreen.main.bounds)
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        if (PreferenceHelper.getAccessToken() != "") {
            let initialViewController = storyboard.instantiateViewController(withIdentifier: "your View Controller Identifier")
            self.window?.rootViewController = initialViewController
        } else {
            let initialViewController = storyboard.instantiateViewController(withIdentifier: "your View Controller identifier")
            self.window?.rootViewController = initialViewController
        }
        self.window?.makeKeyAndVisible()
        return true
    }

/*
use your view Controller identifier must use it doubles quotes**strong text**

检查nsuser默认值首选项的值完全存储并在初始视图中检查条件控制器问题
查尔斯

0

Swift 5或更高版本#通过此简单代码编写路径视图控制器。如果您使用的是xcode 11或更高版本,请var window: UIWindow?在AppDelegate中进行首次初始化

let rootVC = mainStoryboard.instantiateViewController(withIdentifier: "YOURCONTROLLER") as! YOURCONTROLLER

        navigationController.setNavigationBarHidden(true, animated: true)
        UIApplication.shared.windows.first?.rootViewController = UINavigationController.init(rootViewController: rootVC)
        UIApplication.shared.windows.first?.makeKeyAndVisible()

0

如果您不想更改applicationDidFinish,可以执行以下技巧:

将导航控制器设置为初始视图控制器,并为其分配自定义类“ MyNavigationController”。然后,您可以在viewDidLoad期间调整其根视图控制器-它会覆盖您在情节提要中设置的根视图控制器。

class MyNavigationController: UINavigationController {
    override func viewDidLoad() {
        super.viewDidLoad()
        if !isLoggedIn() {
            viewControllers = [R.storyboard.authentication.loginView()!]
        }
    }

    private func isLoggedIn() -> Bool {
        return false
    }

}

-3

选择要首先打开的视图控制器,然后转到属性检查器。转到初始场景并检查是否为初始视图控制器选项。

现在,这将是您的初始视图控制器,它将在应用程序启动时首先打开。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.