【问题标题】:Storyboard - refer to ViewController in AppDelegateStoryboard - 参考 AppDelegate 中的 ViewController
【发布时间】:2012-01-01 10:48:40
【问题描述】:

考虑以下场景:我有一个基于故事板的应用程序。我将 ViewController 对象添加到情节提要,将此 ViewController 的类文件添加到项目中,并在 IB 身份检查器中指定新类的名称。现在我将如何从 AppDelegate 以编程方式引用这个 ViewController?我已经使用相关类创建了一个变量并将其转换为 IBOutlet 属性,但是我看不到任何能够在代码中引用新 ViewController 的方法 - 任何 ctrl-drag 连接的尝试都不起作用.

即在 AppDelegate 中,我可以像这样访问基本 ViewController

(MyViewController*) self.window.rootViewController

但是故事板中包含的任何其他 ViewController 呢?

【问题讨论】:

标签: iphone objective-c ios uiviewcontroller storyboard


【解决方案1】:

适用于 iOS 13+

在场景委托中:

var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options 
connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    window = UIWindow(windowScene: windowScene)
    let storyboard = UIStoryboard(name: "Main", bundle: nil) // Where "Main" is the storyboard file name
    let vc = storyboard.instantiateViewController(withIdentifier: "ViewController") // Where "ViewController" is the ID of your viewController
    window?.rootViewController = vc
    window?.makeKeyAndVisible()
}

【讨论】:

    【解决方案2】:
        UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Tutorial" bundle:nil];
        self.window.rootViewController = [storyboard instantiateInitialViewController];
    

    【讨论】:

      【解决方案3】:

      通常,系统应该使用情节提要处理视图控制器实例化。您想要的是通过获取对 self.window.rootViewController 的引用来遍历 viewController 层次结构,而不是初始化视图控制器,如果您正确设置了情节提要,则应该已经正确初始化了视图控制器。

      所以,假设你的rootViewController 是一个 UINavigationController,然后你想向它的顶视图控制器发送一些东西,你可以在你的 AppDelegate 的didFinishLaunchingWithOptions 中这样做:

      UINavigationController *nav = (UINavigationController *) self.window.rootViewController;
      MyViewController *myVC = (MyViewController *)nav.topViewController;
      myVC.data = self.data;
      

      在 Swift 中如果会非常相似:

      let nav = self.window.rootViewController as! UINavigationController;
      let myVC = nav.topViewController as! MyViewController
      myVc.data = self.data
      

      您真的不应该使用应用程序委托中的故事板 id 来初始化视图控制器,除非您想绕过故事板的正常加载方式并自己加载整个故事板。如果您必须从 AppDelegate 初始化场景,您很可能做错了什么。我的意思是假设您出于某种原因想要将数据发送到堆栈中的视图控制器,AppDelegate 不应该进入视图控制器堆栈来设置数据。那不关它的事。它的业务是rootViewController。让 rootViewController 处理它自己的孩子!因此,如果我通过在 info.plist 文件中删除对它的引用来绕过系统的正常情节提要加载过程,我最多会使用 instantiateViewControllerWithIdentifier: 实例化 rootViewController,如果它是容器,则可能实例化它的根,比如UINavigationController。您要避免的是实例化已经被情节提要实例化的视图控制器。这是我经常看到的一个问题。简而言之,我不同意接受的答案。这是不正确的,除非海报意味着从 info.plist 中删除情节提要的加载,否则您将加载 2 个情节提要,这是没有意义的。这可能不是内存泄漏,因为系统初始化了根场景并将其分配给窗口,但随后您再次实例化并再次分配它。你的应用开局很糟糕!

      【讨论】:

        【解决方案4】:

        如果您使用XCode 5,您应该使用不同的方式。

        • UIStoryboard 中选择您的UIViewController
        • 转到右上角的Identity Inspector
        • 选中Use Storyboard ID复选框
        • Storyboard ID 字段写入唯一的ID

        然后编写你的代码。

        // Override point for customization after application launch.
        
        if (<your implementation>) {
            UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" 
                                                                     bundle: nil];
            YourViewController *yourController = (YourViewController *)[mainStoryboard 
              instantiateViewControllerWithIdentifier:@"YourViewControllerID"];
            self.window.rootViewController = yourController;
        }
        
        return YES;
        

        【讨论】:

        • 据我所知并且能够尝试,this 是更新后的答案。
        • 情节提要的名称是它的文件名,不带扩展名,因此如果您以这种方式设置项目,它也可以是“Main_iPhone”或“Main_iPad”。
        • 然后我们如何在登录后转换回原来的rootViewController。至少据我对 iOS 7 和 Xcode 5.0.2 的了解 - 将根视图控制器更改回来将立即并且没有动画切换视图层次结构。 UX 团队以更少的成本谋杀了编码人员。
        • 我将如何使用 Swift 做到这一点?
        【解决方案5】:

        查看documentation-[UIStoryboard instantiateViewControllerWithIdentifier:]。这允许您使用您在 IB 属性检查器中设置的标识符从情节提要中实例化视图控制器:

        编辑添加示例代码:

        UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard"
                                                                 bundle: nil];
        
        MyViewController *controller = (MyViewController*)[mainStoryboard 
                            instantiateViewControllerWithIdentifier: @"<Controller ID>"];
        

        【讨论】:

        • 嗨罗宾,谢谢!我查看了这个文档,但是将实例化和初始化这两个词混淆了......这让我们到达那里(在按照你的指示进行操作后:)(该死的回复中缺少代码格式......) UIStoryboard* mainStoryboard = [UIStoryboard storyboardWithName: @ “MainStoryboard”捆绑包:无]; MyViewController* thisController = (MyViewController*) [mainStoryboard instantiateViewControllerWithIdentifier: @"myvc"];
        • 我已将您的示例代码添加到答案中,并为正在查看此内容的任何人设置格式。
        • 如果您要制作通用应用程序,请务必使用 MainStoryboard_iPhone/MainStoryboard_iPad 否则您会崩溃。
        • 从委托中,您可以访问由 info.plist 加载的情节提要实例,如下所示:[[[self window] rootViewController] storyboard] 根据文档,这将返回“视图控制器起源的情节提要”。 (如果不是来自故事板,则为 nil)。从那个 UIStoryboard* 你可以使用@RobinSummerhill 提到的实例化调用。请注意,Storyboard 会在需要时实例化您的 viewController(scenes)的新实例,并且不会重复使用之前查看过的实例。
        • 我相信 OP 想知道如何获取当前运行的 vc。不是如何加载一个。正如他解释的那样,你以前可以说这个 self.window.rootViewController,现在你不能再说了。
        猜你喜欢
        • 2012-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-13
        • 1970-01-01
        • 2012-08-12
        • 2012-07-07
        相关资源
        最近更新 更多