After implementing state restoration in my app I am having difficulty with my view controllers.
My issue is that in viewDidLoad of the view controller I am attempting to restore the self.naigationController property, which is nil.
I have set breakpoints in the viewDidLoad method of each view controller that comes before the one I am trying to restore. In the first view controller, the navigation controller is found. In every one after that, including the one I am trying to restore, the navigation controller is nil.
This is causing bugs where I cannot set the navigation bar visibility, views are misplaced, etc.
Does anyone have an idea why this might be?
Try moving the code to viewWillAppear.
Unlike viewDidLoad, viewWillAppear can get called multiple times, so take care to make sure your code is idempotent. That is, make sure the results are what you expect if the method is called repeatedly. For example, make sure you don't initialize a new subview if the view has already been added.
The navigationController property searches up the parentViewControllers to find one of class UINavigationController. Only view controllers loaded from the storyboard can find their navigation controller at the viewDidLoad time (and in awakeFromNib). It is likely you are instantiating a new instance of the view controller during the restore process rather than using the one that was created by the storyboard. The solution is either to help the restore process find the existing view controller if it has changed paths e.g. because of split controller orientation change (via viewControllerForRestorationPathComponents in your app delegate) although that comes with its own set of issues, or make your view controller work by not requiring the nav controller in the viewDidLoad, e.g. put it in the viewWillAppear as others have suggested.
Related
I haven't really seen any resource that gives a good and simple explanations on relationship among window, rootviewcontroller, childviewcontroller, navigationcontroller and how they piece together in iOS development. Anyone one knows how to put this in a easy-to-understand way or any online resource or book that does a good job in explaining it?
Per the documentation on UIWindow:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIWindow_Class/
A UIWindow object provides the backdrop for your app’s user interface and provides important event-handling behaviors. Windows do not have any visual appearance of their own, but they are crucial to the presentation of your app’s views.
Xcode typically provides your application's main window, but you can add more if you need to.
From the documentation link you can see that UIWindow is actually a UIView
Enter your first view controller. Like providing a main window, when you start a new Project in Xcode the project template usually wires up your initial view controller, which as the name implies controls a view (UIView).
You could call this initial view controller your RootViewController but if you get a handle on the UIWindow you could just as easily swap out the current initial view controller's view for any other view controller view you like.
That probably doesn't help with hard and fast rules for things, but if I understand what you are asking, RootViewController is likely the initial view controller for you application. For example, if you are using Storyboards, Xcode typically makes Main.storyboard, you will see a gray arrow pointing to the UIViewController representation.
This is pointing to the Storyboards Initial View Controller. You can verify this from the Attributes Inspector. Select the view controller then select the attribute inspector:
So that's basically RootViewController. ChildViewController is just any other view controller that is a child of a view controller.
I assume what you are referring to is:
addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:
You can read more about these methods here:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/
Under Implementing a Container View Controller
The quick gist of it is, A View Controller controls a view. View's can have subviews. Those subviews can come from other View Controllers. The methods outlined above pretty much just enable things like viewWillAppear, or viewWillDiappear to be called on the child view controller automatically when those methods are invoked on the parent view controller.
Per the docs:
By default, rotation and appearance callbacks are automatically forwarded to children. You may optionally override the shouldAutomaticallyForwardRotationMethods and shouldAutomaticallyForwardAppearanceMethods methods to take control of this behavior yourself.
a NavigationController is just like any other View Controller. It contains some special behavior for transitioning between views, but like other View Controllers it has a View (UIView) that it manages. A navigation controller could be your Initial View Controller / RootViewController just as any other View Controller can be, it all just depends on what you are trying to do. For example, a simple app that is just a list view, where you can tap an item and get a detail view could be constructed as:
1) Initial View Controller -> NavigationController
2) The NavigationController's first ViewController (Apple calls this a RootViewController) would then be a TableViewController.
3) Selecting a TableCell in the TableView (TableViewController manages a TableView) would then transition you to your Detail View Controller. The Navigation Controller knows how to do all that Sliding back and forth drama.
That's a pretty simplistic overview you can search the internet/youtube for more full featured tutorials outlining the same thing in more detail.
Example: https://www.raywenderlich.com/113388/storyboards-tutorial-in-ios-9-part-1
It's worth your time to do a few of these to get your bearings. Yes, it will likely cost you a few hours of your day. Take heart, everyone who ever started doing iOS development had to go though the same thing. =)
Why every time I change UIViewController embed in UINavigationController using show push in storyboard ViewDidLoad is called?
It hasn't to be called only once or I have to check programmatically if it is already loaded?
Another relative question:
In the following best practices found here in StackOverflow that user are talking about init method, but if my ViewController are loaded by storyboard where I have to initialise my properties?
Best practices
Remember not to do view controller initialisation in viewDidLoad. This is a common mistake. For stuff that should only happen once when the view controller is loaded, do it in one of the controller's init methods.
viewDidLoad is called first time when the viewController's view is loaded, (either by accessing the view controller's view or by presenting a view controller via modal presentation or via a push presentation). Once the view controller is loaded, viewDidLoad will not get called again. If you want to use init method, you need to use initWithCoder for the things that are from storyboard.
I have a side menu controller that is part of the rootViewControllerI never remove it from there and when it slides off screen - its just an animation that updates its frame details.
Is it possible to update this view, while it is not displayed on scene? I have a UiTableView in there and I would like to reload it while it is off screen - so when the user slides out the screen, its already populated with new content.
My first approach was a delegate - however, the delegate method doesn't get fired and I believe this is due to it being off screen. But, I somehow think side its in UIWindow it is never really deallocated like a normal view when it leaves the screen?
Edit
I am using this Github project for the menu.
The view I want to update is in a UINavigation controller, one level deep. I can get the current instance of it - however, the delegate method doesn't trigger.
It seems to me that you are going with something like this. Even if not, look at the example. Here RootViewController is always alive and you move one viewcontroller to parent view controller and remove other one.
I have two ways to fix it:
If you are removing first view from parent view controller. Don't remove it. So the controller is still live and use delegates to trigger the event.
Remove first view controller then use Root view controller to get the updates and once the previous view controller loads back take updates from root view controller and update this one.
Hope it can atleast give you an idea.
I'm using Storyboards. I need to init instance variables of UIViewController just once. viewDidLoad and awakeFromNib fire each time I open a viewController from menu (I'm using SWRevealViewController for sidebar menu as in Facebook app). Is it normal for awakeFromNib to fire many times or is it SWRevealViewController bug?
So is there some special init method or do I need to set instance variables from outside of viewController (in AppDelegate?)
EDIT:
I found this question
SWRevealViewControllerSegue, reusing Viewcontrollers
I didn't realize that each time you show a controller via segue it creates a new instance of controller. So what does it mean? Does it mean that if I have a table of data loaded from web API - it will be reloaded each time I go back and forth through segue?.. Doesn't seem very effective to me. Is it normal for iOS?
View controllers are either recreated or reused depending on the situation. When you push a view controller onto a navigation controller's stack, you almost always push a new instance. When using a tab bar controller, the view controllers it contains are often held in memory. To avoid excessive memory consumption, it makes sense that view controllers are cleaned up when possible, rather than held around.
In the case of your SWRevealViewController then yes, it sounds as though your view controller should be recreated every time. You ideally shouldn't be directly loading any data from a web API within your view controller; instead, extract your API calls and models into separate classes that you can reuse from any view controller. Then, rather than reloading the data over the network when your view controller loads, you just need to fetch the latest data from your model / store class.
When using tabs with storyboard in iOS 5, some of them may take quite a long time to initialize when switching to it (for example, a tab containing GLKViewController).
This happens because an amount of work in viewDidLoad method in this controller could be very big.
Is there a way to initialize particular view controller (and call it's viewDidLoad method) defined in the storyboard at early stage - when an application starts? Having done this, the delay should be eliminated.
Are you sure it's the view controller's instantiation and not the viewDidLoad method? The view controllers are probably all created when the storyboard is unpacked, but a view controller tries to delay loading its actual view object as long as possible; viewDidLoad isn't called until the view property of your UIViewController subclass is accessed.
So a way around this could be to manually access the view property:
__unused CGRect frame = [[tabBarController.viewControllers objectAtIndex:index] view].frame;
If the slowdown is, in fact, in the instantiation and the view controller isn't being created until you switch to that tab, then you'll have do force the view controller to be instantiated by accessing it programmatically, like in the above example.
Calling the frame of the vewcontroller or the .view property will most likely work,
but i dont advice you to mess up with the viewcontroller initializations and view settings
For the following reasons
changes you make will not be standard, they will be tricks and hacks that will later on get out of hand
changes that you make will not be carried with you easily to other projects you create
If i faced a problem like this i would create the GLKViewController separately for example in the app delegate and held it there, untill the viewDidLoad gets called in the viewController, then i would move this initilized GLKViewController to the viewController