从Storyboard实例化视图控制器 vs 创建新实例

44
什么是从故事板实例化视图控制器和创建新实例之间的功能差异?例如:
#import "SomeViewController.h"

...

SomeViewController *someViewController = [SomeViewController new];

对比

#import "SomeViewController.h"

...

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

SomeViewController *someViewController = [storyboard instantiateViewControllerWithIdentifier:@"SomeViewController"];

无论哪种情况,someViewController是否实际上是相同的东西?
7个回答

36

主要区别在于如何实例化UIViewController的子视图。

在第二种情况下,你在Storyboard中创建的所有视图都将自动实例化,并且所有的插座和操作将按照你在Storyboard中指定的设置进行设置。

而在第一种情况下,所有这些都不会发生;你只会得到原始对象。你需要分配和实例化所有的子视图,使用约束或其他方式对其进行布局,并自己连接所有插座和操作。苹果建议通过覆盖UIViewControllerloadView方法来完成此操作。


16
在第二种情况下,视图控制器将从故事板加载其视图,你将感到高兴。
在第一种情况下,它不会。除非你采取了其他步骤(比如覆盖loadView或viewDidLoad,或创建名为SomeViewController.xib的xib文件),否则你只会得到一个空白的白色视图,让你感到悲伤。

当我使用第二种情况时,实例化和控制器加载之间有很大的延迟,我想知道为什么会这么糟糕?这是来自iPhone 4S的日志:`2015-03-03 20:42:39.692 App[5652:986462] 实例化2015-03-03 20:42:43.506 App[5652:986462] ViewDidLoad` 几乎需要4秒钟。还没有尝试另一种方式,但也许你也会遇到这个问题? - Dima Deplov
视图控制器会在需要时加载其视图,因为某些东西要求其“视图”属性。这通常不会发生,直到系统实际想要将视图放在屏幕上。在您实例化VC和加载其视图之间发生了什么?您是否执行一些缓慢的操作,例如网络请求? - rob mayoff
不好意思,我什么也没做,只是等待加载。我还尝试了执行到这个视图控制器的转场,但是加载时间一样=/ 我想这可能与视图控制器有关。我会尝试找出问题所在,如果您不介意的话,我会再次在这里写下来。 - Dima Deplov
如果您感兴趣,这个问题是由于将自定义字体设置到文本字段中,但该字体未复制到设备上,从而导致了延迟。rdar://20028250 - Dima Deplov

5
在Swift中,您可以使用以下方式进行相同操作:
var someVC = self.storyboard?.instantiateViewControllerWithIdentifier("SomeViewController") as! SomeViewController

您需要在Storyboard中将标识符赋予SomeViewController,并勾选“使用Storyboard ID”复选框。


3
这两种情况不同。在storyboard中,可能已经布置好一些UI元素,并通过storyboard设置了约束和属性。当您通过storyboard实例化视图控制器时,将获取有关这些子视图的位置和属性的所有指令。如果您只使用[SomeViewController new],则未获取storyboard为视图控制器提供的所有指令。
一个很好的测试是将UIViewController添加到storyboard中,然后将红色视图拖到其上。使用这两种方法实例化它,看看它们之间的区别。

2
simple swift 3 extension   
 fileprivate enum Storyboard : String {
        case main = "Main"
    }

    fileprivate extension UIStoryboard {
        static func loadFromMain(_ identifier: String) -> UIViewController {
            return load(from: .main, identifier: identifier)
        }

        static func load(from storyboard: Storyboard, identifier: String) -> UIViewController {
            let uiStoryboard = UIStoryboard(name: storyboard.rawValue, bundle: nil)
            return uiStoryboard.instantiateViewController(withIdentifier: identifier)
        }
    }

    // MARK: App View Controllers

    extension UIStoryboard {
        class func loadHomeViewController() ->  HomeViewController {
            return loadFromMain("HomeViewController") as! HomeViewController
        }
    }

0

另一个需要检查的事情是,如果抛出错误的视图控制器具有storyboardIdentifier,您可以检查storyboard xib文件。

在我的情况下,标识符丢失了,当我添加它时,错误停止了。


0

如果您不想使用instantiateViewControllerWithIdentifier实例化新的VC,而是从AppDelegate访问故事板创建的实例:

  1. 在AppDelegate.h中创建一个属性,以便可以从使用它的类中访问@property (nonatomic, strong) myViewControllerClass*vC;
  2. 在myViewControllerClass.m中的viewDidLoad中,我访问AppDelegate的共享实例,并使用self填充属性:[AppDelegate sharedInstance].vC = self;

我不得不在一个复杂的故事板中使用这个解决方案,仍然无法摆脱这样一个事实,即我无法通过简单地寻址它们的标识符来访问所有(或至少我需要的)对象。


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