iOS state preservation and container views
Asked Answered
F

2

20

I have a view controller in a storyboard that is using a container view. Both have restoration identifiers set. The parent is being saved and restored just fine. The child however is not. Neither -encodeRestorableStateWithCoder: or -decodeRestorableStateWithCoder: are being called on the child view controller.

What's the correct way to save child view controllers that are created with a view container? I can save the child view controller in the parents -encodeRestorableStateWithCoder:, which will cause it to be saved, but I don't have a way of using it during a restore.

Forage answered 3/4, 2013 at 23:17 Comment(4)
Did you add the child to the parent with addChildViewController: ?Knurled
did you call the didMoveToParentViewController: method after you added the child view controller to the parent view controller?Concentre
The container view should be doing that automatically. I'm getting a reference to it from the parents childViewControllers.Forage
Experienced the same problem I add vc in containment via storyboard, but it didn't get any call. What is the proper way to do this ?Homology
D
21

Container view controller "does not automatically save references to any contained child view controllers. If you are implementing a custom container view controller, you must encode the child view controller objects yourself if you want them to be preserved".

There are simple rules that i found:

1.Embedded(child) view controller should already be created and added to parent view controller at the state preservation process. So, do not have to do anything if you use storyboard otherwise you'll have to instantiate child view controller and add it manually:

-(void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"Did load");
    MyChildViewController *childViewController = [MyChildViewController new];
    [self addChildViewController:childViewController];
    [childViewController didMoveToParentViewController:self];
    self.childVC = childViewController;
}

You can add child view at -viewDidLoad or later. Use self.childVC.view.frame = [self frameForChildController]; [self.view addSubview:self.childVC.view]; for this.

2.You no need to save the child view controller in the parent's -encodeRestorableStateWithCoder: himself, but you should encode a reference to that object using -encodeObject:forKey:. If you have reference you can do it like this:

-(void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
    NSLog(@"Encode");
    UIViewController *childViewController = self.childVC;
    [coder encodeObject:childViewController forKey:@"ChildVC"];
    [super encodeRestorableStateWithCoder:coder];
}

see https://mcmap.net/q/126202/-access-container-view-controller-from-parent-ios to get reference to child VC if you use Storyboard. Or you can write something simple like this:

-(void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
    NSLog(@"Encode");
    UIViewController *childViewController = [self.childViewControllers objectAtIndex:0]; //[self.childViewControllers lastObject];
    [coder encodeObject:childViewController forKey:@"ChildVC"];
    [super encodeRestorableStateWithCoder:coder];
}

3.Embedded(child) view controller should already be created and added to parent view controller at the state restoration process. So, if you did everything in the first paragraph, there is nothing more to do here.

4."In this case, however, we do not decode child view controller. We could, but in fact we don't need it.The MyChildViewController object will restore its own state. We only encoded this reference in order to get the runtime to walk the chain down to the MyChildViewController instance and do save-and-restore on it".

-(void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
    NSLog(@"Decode");
    [super decodeRestorableStateWithCoder:coder];
}

This book helps me for understanding state preservation with container views. Also look for a good example for this book

Defer answered 24/6, 2013 at 7:32 Comment(2)
Summing this up, if you use storyboard container view (the one with embed segue), then all you need to do is to call encodeObject:forKey: with a child from parent's encodeRestorableStateWithCoder:. The best way to obtain the reference to the child is in prepareForSegue:sender: for the embed segue. No need to decode the child manually, since the restoration process will find the already existing child and restore its state from the archive. However, if you do not use storyboard (i.e. add the children manually) then you might need to implement that finding mechanism too.Saiga
You say that the embedded child controller should be already created and added to parent view at the state restoration process. But how can we access it? Because if it's already instantiated, then one should not do it again in viewDidLoad as you showedFiligree
G
-1

I think the answer is in the documentation It is said:

" The UIViewController class saves a reference to the presented view controller and the storyboard (if any) that was used to create the view controller. The view controller also asks the views in its view hierarchy to save out any relevant information. However, this class does not automatically save references to any contained child view controllers. If you are implementing a custom container view controller, you must encode the child view controller objects yourself if you want them to be preserved."

So you could do something like that:

-(void)encodeRestorableStateWithCoder:(NSCoder *)coder {
    [super encodeRestorableStateWithCoder:coder];
    [self.myChildViewController encodeRestorableStateWithCoder:coder];
}

-(void)decodeRestorableStateWithCoder:(NSCoder *)coder {
    [super decodeRestorableStateWithCoder:coder];
    [self.myChildViewController decodeRestorableStateWithCoder:coder];
}

And in MyChildViewController do not call super :)

Garaway answered 20/5, 2013 at 10:33 Comment(5)
My question was about using view containers, not child view controllers in general.Forage
Ok in my case I am using container view to add the child view controller. First I am getting a reference to the created child view controller in -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender and then I decode and encode manually as I explained earlier.Garaway
Is there some way to decode state of MyChildViewController, which call [super decodeRestorableStateWithCoder:coder] at own -(void)decodeRestorableStateWithCoder:(NSCoder *)coder? For example I want to use MyChildViewController as a child view controller and at the same time as independent view controller (use as parameter in pushViewController).Malissa
You don't want to lump in the child view controller's state with the parent view controller's, which is what this code will do. Instead, encode the child view controller whole, as a separate object.Lands
This is solution does not actually use the state preservation/restoration mechanism for the child, it simply calls some custom code (that you misleadingly called encodeRestorableStateWithCoder and decodeRestorableStateWithCoder) to save and restore its state as part of the parent's state.Saiga

© 2022 - 2024 — McMap. All rights reserved.