Reputation: 2074
I've a heavy UIViewController
, which loads multiple images in viewDidLoad
(so it's called once, on the first controller access only).
It takes 5-10 seconds for it to load, which I want to reduce, preloading it from somewhere on application start.
I've done my research and tried accessing the view
attribute of the controller for it to load in didFinishLaunchingWithOptions
method of AppDelegate
, and it actually did (viewDidLoad
was called), but when I one the controller itself, viewDidLoad
is called again and all the images are loaded again too.
Current code example is the following:
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
}
How can I preload a huge UIViewController
so it doesn't take so much to load when opened? Data it's holding is completely static, so any solution would apply.
Upvotes: 2
Views: 945
Reputation: 32914
wvteijlingen's answer should be the way to go, extracting the heavy (business) logic from the controller into a dedicated class is the optimal solution. However if refactoring would be complicated, then you can follow the approach described below.
The controller instance you create in didFinishLaunchingWithOptions
is lost once the method exits, so its view and the contents loaded by the view are discarded. If you want to reuse that instance, you can store it into a property 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
}
And from this point on you can access the view controller when needed: UIApplication.shared.delegate as! AppDelegate).modeDescriptionViewController
.
Upvotes: 1
Reputation: 120082
Although your solution is not going to help you to achieve faster load and you MUST make your heavy works async
:
Your explained problem is when you instantiateViewController
it creates a new view controller that is completely different from the one you will see later. So instead of that, you must get it. One way you can do that is to access it from the main window
in appDelegate:
let controller = window?.rootViewController as? modeDescriptionViewController
Then you can force it to load it’s view (not recommended at all)
Controller.loadView()
And then it calls viewDidLoad automatically
Tip: If window
is not loaded, you can call makeKeyAndVisible()
on it.
Upvotes: 0
Reputation: 15238
I would suggest extracting all the loading logic from the controller. Create a DataLoader
class (or struct) that does the loading and stores the loaded data.
Instantiate a DataLoader
in your AppDelegate and instruct it to load the data, decoupled from the view controller. Then pass this instance to the controller, so the controller can access the data from the loader.
You will probably need to implement some state tracking to make sure that when you ask the loader for the data, it will either begin loading, or wait if loading is already in progress.
A a plus, you can also implement loading in a background queue so it doesn't block the UI.
Upvotes: 3