在应用程序启动时预加载 ViewController
Preload ViewController on application start
我有一个很重的 UIViewController
,它在 viewDidLoad
中加载多个图像(所以它只在第一个控制器访问时调用一次)。
加载它需要 5-10 秒,我想减少它,在应用程序启动时从某个地方预加载它。
我已经完成研究并尝试访问控制器的 view
属性,以便它加载到 AppDelegate
的 didFinishLaunchingWithOptions
方法中,它确实做到了(viewDidLoad
被调用),但是当我使用控制器本身时, viewDidLoad
被再次调用并且所有图像也被再次加载。
当前代码示例如下:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "modeDescriptionViewController")
let _ = vc.view // viewDidLoad is called here
return true
}
我如何预加载一个巨大的 UIViewController
以便在打开时不需要太多加载?它持有的数据是完全静态的,因此任何解决方案都适用。
我建议从控制器中提取所有加载逻辑。创建一个 DataLoader
class(或结构)来执行加载并存储加载的数据。
在您的 AppDelegate 中实例化一个 DataLoader
并指示它加载数据,与视图控制器分离。然后将这个实例传递给控制器,这样控制器就可以从加载器访问数据了。
您可能需要实施一些状态跟踪,以确保当您向加载程序请求数据时,它将开始加载,或者如果加载已经在进行中则等待。
还有一个好处,您还可以在后台队列中实现加载,这样它就不会阻塞 UI。
虽然您的解决方案不会帮助您实现更快的加载速度,但您必须完成繁重的工作 async
:
您解释的问题是,当您 instantiateViewController
它创建了一个与您稍后将看到的完全不同的新视图控制器。因此,您必须 get 而不是那样。一种方法是从 appDelegate 中的主 window
访问它:
let controller = window?.rootViewController as? modeDescriptionViewController
然后你可以强制它加载它的视图(完全不推荐)
Controller.loadView()
然后自动调用viewDidLoad
提示:如果window
没有加载,你可以在上面调用makeKeyAndVisible()
。
wvteijlingen 的回答应该是可行的方法,将繁重的(业务)逻辑从控制器提取到专用的 class 中是最佳解决方案。但是,如果重构会很复杂,那么您可以按照下面描述的方法进行操作。
您在didFinishLaunchingWithOptions
中创建的控制器实例一旦方法退出就会丢失,因此它的视图和视图加载的内容将被丢弃。如果要重用该实例,可以将其存储到 属性 of AppDelegate
:
var modeDescriptionViewController: UIViewController!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
modeDescriptionViewController = storyboard.instantiateViewController(withIdentifier: "modeDescriptionViewController")
// loadViewIfNeeded() is cleaner
modeDescriptionViewController.loadViewIfNeeded()
return true
}
从现在开始,您可以在需要时访问视图控制器:UIApplication.shared.delegate as! AppDelegate).modeDescriptionViewController
。
我有一个很重的 UIViewController
,它在 viewDidLoad
中加载多个图像(所以它只在第一个控制器访问时调用一次)。
加载它需要 5-10 秒,我想减少它,在应用程序启动时从某个地方预加载它。
我已经完成研究并尝试访问控制器的 view
属性,以便它加载到 AppDelegate
的 didFinishLaunchingWithOptions
方法中,它确实做到了(viewDidLoad
被调用),但是当我使用控制器本身时, viewDidLoad
被再次调用并且所有图像也被再次加载。
当前代码示例如下:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "modeDescriptionViewController")
let _ = vc.view // viewDidLoad is called here
return true
}
我如何预加载一个巨大的 UIViewController
以便在打开时不需要太多加载?它持有的数据是完全静态的,因此任何解决方案都适用。
我建议从控制器中提取所有加载逻辑。创建一个 DataLoader
class(或结构)来执行加载并存储加载的数据。
在您的 AppDelegate 中实例化一个 DataLoader
并指示它加载数据,与视图控制器分离。然后将这个实例传递给控制器,这样控制器就可以从加载器访问数据了。
您可能需要实施一些状态跟踪,以确保当您向加载程序请求数据时,它将开始加载,或者如果加载已经在进行中则等待。
还有一个好处,您还可以在后台队列中实现加载,这样它就不会阻塞 UI。
虽然您的解决方案不会帮助您实现更快的加载速度,但您必须完成繁重的工作 async
:
您解释的问题是,当您 instantiateViewController
它创建了一个与您稍后将看到的完全不同的新视图控制器。因此,您必须 get 而不是那样。一种方法是从 appDelegate 中的主 window
访问它:
let controller = window?.rootViewController as? modeDescriptionViewController
然后你可以强制它加载它的视图(完全不推荐)
Controller.loadView()
然后自动调用viewDidLoad
提示:如果window
没有加载,你可以在上面调用makeKeyAndVisible()
。
wvteijlingen 的回答应该是可行的方法,将繁重的(业务)逻辑从控制器提取到专用的 class 中是最佳解决方案。但是,如果重构会很复杂,那么您可以按照下面描述的方法进行操作。
您在didFinishLaunchingWithOptions
中创建的控制器实例一旦方法退出就会丢失,因此它的视图和视图加载的内容将被丢弃。如果要重用该实例,可以将其存储到 属性 of AppDelegate
:
var modeDescriptionViewController: UIViewController!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
modeDescriptionViewController = storyboard.instantiateViewController(withIdentifier: "modeDescriptionViewController")
// loadViewIfNeeded() is cleaner
modeDescriptionViewController.loadViewIfNeeded()
return true
}
从现在开始,您可以在需要时访问视图控制器:UIApplication.shared.delegate as! AppDelegate).modeDescriptionViewController
。