在初始化第一个视图控制器之前运行代码(基于故事板的应用程序)

Posted

技术标签:

【中文标题】在初始化第一个视图控制器之前运行代码(基于故事板的应用程序)【英文标题】:Run Code Before First View Controller is Initialized (Storyboard-based App) 【发布时间】:2016-05-05 09:40:56 【问题描述】:

我的应用程序需要在每次启动时执行一些清理任务 - 删除本地存储的旧数据 - 在显示初始视图控制器之前。

这是因为初始视图控制器在初始化时加载现有数据,并将其显示在表格视图中。

我设置了几个断点,发现初始视图控制器的初始化程序 (init?(coder aDecoder: NSCoder)) 在之前 application(_:didFinishLaunchingWithOptions) - 甚至在application(_:**will**FinishLaunchingWithOptions) 之前运行,准确地说。

可以将清理代码放在视图控制器初始化程序的最顶部,但我想将清理逻辑与任何特定屏幕分离。 该视图控制器有一天可能最终不会成为最初的视图控制器。

覆盖应用程序委托的init() 方法并将我的清理代码放在那里确实完成了工作,但是......

问题:

难道没有更好/更优雅的方式吗?


注意:供参考,相关方法的执行顺序如下 如下:

    AppDelegate.init() ViewController.init() AppDelegate.application(_:willFinishLaunchingWithOptions:) AppDelegate.application(_:didFinishLaunchingWithOptions:) ViewController.viewDidLoad()

澄清:

清理任务并不冗长,也不需要异步运行:相反,我希望我的初始视图控制器在完成之前甚至没有实例化(我知道如果启动时间过长,系统会杀死我的应用程序。但是,它只是删除了一些本地文件,没什么大不了的。)。

我问这个问题主要是因为在故事板时代之前,应用启动代码看起来像这样:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];

    // INITIAL VIEW CONTROLLER GETS INSTANTIATED HERE 
    // (NOT BEFORE):
    MyViewController* controller = [[MyViewController alloc] init];

    self.window.rootViewController = controller;

    [self.window makeKeyAndVisible];
    return YES;

...但是现在,因为主情节提要加载发生在幕后,视图控制器在任何应用程序委托方法运行之前被初始化。

不是在寻找需要将自定义代码添加到我的初始视图控制器的解决方案。

【问题讨论】:

【参考方案1】:

我不确定是否有更优雅的方式,但肯定有其他方式......

我希望我的初始视图 控制器在完成之前甚至不会被实例化

这不是问题。您所要做的就是从Info.plist 中删除UIMainStoryboardFileNSMainNibFile 键,它告诉UIApplicationMain 应该加载什么UI。随后,您在 AppDelegate 中运行“清理逻辑”,完成后您自己启动 UI,如示例中所示。

另一种解决方案是创建UIApplicationMain 的子类并在加载 UI 之前在其中运行清理。

请参阅下面的应用生命周期

【讨论】:

这不是一个解决方案,因为放弃 main.storyboard 会使状态恢复变得更加困难【参考方案2】:

    您可以在初始 ViewController 上添加一个 UIImageView,其中将包含您应用的启动图像。

    在 viewDidLoad()... 中使 imageview.hidden 属性为 False... 您是否进行清理操作并在完成清理任务时使 imageview.hidden 属性为 TRUE。

这个用户不会知道你在做什么工作,这种方法被用于许多公认的应用程序。

【讨论】:

您的问题是“没有更好/更优雅的方式吗?”,我向您展示了其中一种方式,休息是您的选择,您可以将任务划分为不同的模型并定义它。正如你刚才提到的,视图控制器在初始化时会加载现有数据,所以如果你想控制它,这是一种替代方法。【参考方案3】:

我遇到了一个非常相似的情况

我需要运行代码的地方,只有在 didFinishLaunchingNotification 之后才准备好

我想出了这种模式,它也适用于状态恢复

var finishInitToken: Any?

required init?(coder: NSCoder) 
    
    super.init(coder: coder)
    
    finishInitToken = NotificationCenter.default.addObserver(forName: UIApplication.didFinishLaunchingNotification, object: nil, queue: .main)  [weak self] (_) in
        self?.finishInitToken = nil
        self?.finishInit()
    
    


func finishInit() 
    ...


override func decodeRestorableState(with coder: NSCoder) 
    
    // This is very important, so the finishInit() that is intended for "clean start"
    // is not called
    if let t = finishInitToken 
        NotificationCenter.default.removeObserver(t)
    
    finishInitToken = nil
    

你可以为 willFinishLaunchingWithOptions 发出通知

【讨论】:

以上是关于在初始化第一个视图控制器之前运行代码(基于故事板的应用程序)的主要内容,如果未能解决你的问题,请参考以下文章

来自故事板的表格视图单元原型未初始化子视图

如何在iphone中从一个视图转到故事板的第一个视图

如何在 XCode 中预览故事板的不同视图控制器

带有故事板的 UISplitViewController

你如何摆脱swift中主要故事板的代码?

用于通用故事板的 PSD