Goal: I'm trying to restore state on a tab controller-based app (with navigation controllers on each tab).
Problem: On relaunch, the selected tab seems to be restored as expected, but the navigation hierarchy inside said tab is not.
Development:
application(_:shouldSaveApplicationState:)
and application(_:shouldRestoreApplicationState:)
.I run then app, switch to the second (right) tab, hit home, terminate. o relaunch, the right tab is displayed (as expected). So far so good.
I run the app, and restoration still works. Still good.
Next, I add a "detail" view controller; its class is a custom subclass of UIViewController
to the storyboard, with properties to configure the contents of a debug label and its view's background color.
I placed a "Show Detail..." button on each of the tabs' top view controllers, and create a segue from each into the (shared) detail view controller. So now my storyboard looks like a hexagon (also, both segues have identifiers set in Interface Builder). So, both left and right top view controllers share the same type of "detail" view controller. On show, it is configured so as to distinguish from where it has been pushed (see next point).
On each of the top view controllers' prepareForSegue(_:sender:)
method, I configure the pushed detail view controller differently: Different text and background color ("left" and blue, and "right" and red, respectively).
I added code to the detail view controller to save and restore the state of the text and background color properties: encodeRestorableStateWithCoder(_:)
and decodeRestorableStateWithCoder(_:)
. Also, I implemented viewDidLoad()
so as to reflect those properties' values in the view. Whenever it is instantiated and pushed into the navigation through a segue, the properties are first set and then used to configre the view in viewDidLoad()
. Whenever it is instantiated during restoration, the properties are set in decodeRestorableStateWithCoder(_:)
and similarly used in viewDidLoad()
.
...but when I run this code, the last selected tab is restored but only up to the top view controller -left or right-, not the detail. Interestingly, the background color last set to the detail view controller flashes for an instant.
I placed break points in encodeRestorableStateWithCoder(_:)
and decodeRestorableStateWithCoder(_:)
, but only the first of them is executed (encode).
application(_:viewControllerWithRestorationIdentifierPath:coder:)
(returning always nil, but logging the path components passed).The documentation is not very clear on whether this method is needed or not, and in any case all view controllers except the detail seem to be restored perfectly even without it. I added code to instantiate each view controller based on the last path component (i.e., that controller's restoration ID) and returning it.
Now, the decodeRestorableStateWithCoder(_:)
is called, but the navigation still goes back to the tab's top view controller after a split second.
So, what is going on? What am I missing to implement State Preservation and Restoration in a Tab Bar + Navigation Controller app?
A tab bar controller is a powerful UI component for iOS apps. It's a container view, and you use it to group view controllers together. They give your app's user access to the most important screens of your app.
To add a tab, first drag a new View Controller object to the storybard. Next control-drag from the tab bar controller to new view controller and select view controllers under Relationship Segue . Your tab bar controller will update with a new tab.
FIXED: So, there were several problems with my code...
It turns out that in my case, I do not need to implement application(_:viewControllerWithRestorationIdentifierPath:coder:)
. (see the comments of this answer)
My implementations of encodeRestorableStateWithCoder(_:)
and decodeRestorableStateWithCoder(_:)
were not calling super
(as suggested in the accepted answer to the question above).
finally, I got the right view controller (detail) to appear, but its subviews' state (text label contents and main view background color) were in the initial, empty state (not being restored to their last state -i.e., text label contents and bg color). As mentioned in this question, the viewDidLoad() is not called right after decodeRestorableStateWithCoder(_:)
(like I assumed), so instead I call a common method from both viewDidLoad()
and decodeRestorableStateWithCoder(_:)
to update the UI.
As usual, I rushed to post a question before searching or trying enough modifications in my code (my apologies...).
I hope this at least helps someone else.
As usual, I'll wait a couple of days before accepting my own answer, in case somebody sheds additional light.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With