Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is parentViewController always a Navigation controller?

I was kind of scratching my head at this a week ago, and now with a little bit more Cocoa experience under my belt I feel like I have an inkling as to what might be going on.

I'm making an application that is driven by a UINavigationController. In the AppDelegate, I create an instance of this class, using "page 1" as the Root View Controller.

UINavigationController *aNavigationController = [[UINavigationController alloc] 
     initWithRootViewController:page1ViewController];

Now here's where I'm having the problem. From "page 1" I'd like to use a modal view controller that slides over the interface and then disappears once the user has made an edit. I do that using code like this, inside of Page1ViewController:

[self presentModalViewController:myModalViewController animated:YES];

When the Modal View Controller is gone, I want a value on "Page 1" to change based on what the user entered in the Modal View Controller. So, I wrote some code like this, which resides in the Modal View Controller:

[self.parentViewController dismissModalViewControllerAnimated:YES];
[self.parentViewController doSomethingPleaseWithSomeData:someData];

The update to page 1 wasn't happening, and it took me a long time to realize that the "doSomethingPleaseWithSomeData" message was not being sent to Page1ViewController, but the Navigation Controller.

Is this always to be expected when using Navigation Controllers? Did I perhaps configure something improperly? Is there an easy way to get at the View Controller that I want (in this case, Page1ViewController).

like image 967
bpapa Avatar asked Oct 28 '08 19:10

bpapa


3 Answers

I would recommend using the delegation pattern to solve your problem. Create a property

@property (nonatomic, assign) id <MyModalViewDelegate> delegate;

And a corresponding protocol

@protocol MyModalViewDelegate
@optional
    - (void)myModalViewControllerDidFinish:(MyModalViewController *)aModalViewController;
@end

When the user finishes with your view (e.g. taps the save button), send this message:

if ([self.delegate respondsToSelector:@selector(myModalViewControllerDidFinish:)])
    [self.delegate myModalViewControllerDidFinish:self];

Now, set the delegate to the view controller that should manage the whole thing, and it will be notified when the view controller is finished. Note that you'll need your view controller to dismiss the modal view controller. But, logically, that makes sense, since it was the object that presented the modal view controller in the first place.

This is how Apple solves this problem in, for example, the UIImagePickerController and UIPersonPickerController.

like image 130
Alex Avatar answered Sep 19 '22 17:09

Alex


There are a couple of ways you can handle this. The simplest is probably just to add a UIViewController property into myModalViewController and set it to page1Controller before you present it:

myModalViewController.logicalParent = self; //page1Controller
[self presentModalViewController:myModalViewController animated:YES];

Just make sure you add the appropriate instance variable @property, and @synthesize for logicalParent to myModalViewController, then you will have a way to communicate data back to the ViewController that triggered the modal dialog. This is also for passing data back and forth between different levels of navigation before you push and pop them on the stack.

The one important thing to worry about when doing this is that it is easy to get retain loops if you are not careful. Depending on exactly how you structure this you might need to use assign properties.

like image 36
Louis Gerbarg Avatar answered Sep 22 '22 17:09

Louis Gerbarg


I just ran into this same problem. It definitely seems that if you put a UIViewController embedded in a NavigationController, then when, from that UIViewController you present another UIViewController modally, the presentee thinks that the presenter is the NavigationController. In other words, parentViewController is incorrect.

I bet this is a bug: either that, or the documentation seems incomplete. I will inquire.

like image 38
Michael Avatar answered Sep 21 '22 17:09

Michael