iOS: Dismissing and Presenting ModalViewController without access to its Parent ViewController - iphone

Background: I would like to dismiss a modalView that I have presented earlier and right away present the same viewController that I just dismissed with new information.
Problem: I have not been very successful in doing so without an explicit pointer to the parent ViewController that presented the first ViewController modally. I am trying to write this class that works without messing around with the previous viewController's code.
Possible lead: There are couple of things I have been experimenting with:
1.) Trying to get access to the parent ViewController, which at this time I don't know how to.
2.) Once access to the parent is gained, I can simply apply the following code:
UIViewController* toPresentViewController = [[UIViewController alloc] init];
[self dismissViewControllerAnimated:YES completion:^{
[parentViewControllerAccessor presentModalViewController:toPresentViewController animated:YES];
}];
In theory this should work given the access to parent viewController. I am open to other ways of doing this.
Assumption: You do not have permission to change any code in the parent ViewController.

Your code looks like it should work. If you are using iOS 5 there is a UIViewController property called presentingViewController.
#property(nonatomic, readonly) UIViewController *presentingViewController;
So you can use this property to get the view controller that presented your modal controller.
Note: In iOS 4 parentViewController would be set to the presenting controller, so if you are supporting both iOS 4 and 5 you will have to check the OS version first to decide which property to access. In iOS 5 Apple have fixed this so that parentViewController is now exclusively used for the parent of contained view controllers (see the section on Implementing a Container View Controller in the UIViewController documentation).
Edit: Regarding accessing self.presentingViewController from within the block: By the time the block is called (after the modal view controller is dismissed) the presentingViewController property may get set to nil. Remember that self.presentingViewController inside the block gives the value of the property when the block is executed, not when it was created. To protect against this do the following:
UIViewController* toPresentViewController = [[UIViewController alloc] init];
UIViewController* presentingViewController = self.presentingViewController;
[self dismissViewControllerAnimated:YES completion:^
{
[presentingViewController presentModalViewController:toPresentViewController animated:YES];
}];
This is necessary not because self is gone/dismissed (it is safely retained by the block), but because it is no longer presented, therefore its presentingViewController is now nil. It is not necessary to store the presentingViewController anywhere else, the local variable is fine because it will be retained by the block.

You could accomplish this using notifications.
For example, fire this notification from outside the modal view when you want it to be dismissed:
[[NSNotificationCenter defaultCenter] postNotificationName:#"dismissModalView"
object:nil
userInfo:nil];
And then handle that notification inside your modal view:
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(dismissMe:)
name:#"dismissModalView"
object:nil];
}
- (void)dismissMe:(NSNotification)notification {
// dismiss it here.
}

the solution for ios5:
-(void)didDismissModalView:(id)sender {
// Dismiss the modal view controller
int sold=0;
if(sold==0){
//Cash_sold.delegate = self;
// Cash_sold.user_amount.text=[NSString stringWithFormat:#"%d",somme];
Cash_sold = [[CashSoldview alloc] initWithNibName:#"CashSoldview" bundle:nil];
CGRect fram1 = CGRectMake(200,20,400,400);
Cash_sold.view.superview.frame = fram1;
Cash_sold.view.frame=fram1;
Cash_sold.modalTransitionStyle= UIModalTransitionStyleCoverVertical;
Cash_sold.modalPresentationStyle=UIModalPresentationFormSheet;
UIViewController* presentingViewController = self.parentViewController;
[self dismissViewControllerAnimated:YES completion:^
{
[presentingViewController presentModalViewController:Cash_sold animated:YES];
}];
}
}

Try the following code:
[self dismissViewControllerAnimated:NO
completion:^{
// instantiate and initialize the new controller
MyViewController *newViewController = [[MyViewController alloc] init];
[[self presentingViewController] presentViewController:newViewController
animated:NO
completion:nil];
}];

Related

Warning: Attempt to present ViewController on ViewController whose view is not in the window hierarchy

I have already looked through related questions but nothing has solved my problem.
I am attempting to use dismissViewControllerAnimated:animated:completion and presentViewControllerAnimated:animated:completion in succession. Using a storyboard, I am modally presenting InfoController via a partial curl animation.
The partial curl reveals a button on InfoController that I want to initiate the MFMailComposeViewController. Because the partial curl partially hides the MFMailComposeViewController, I first want to dismiss InfoController by un-animating the partial curl. Then I want the MFMailComposeViewController to animate in.
At present, when I try this, the partial curl un-animates, but the MFMailComposeViewController doesn't get presented. I also have a warning of:
Warning: Attempt to present MFMailComposeViewController: on
InfoController: whose view is not in the window hierarchy!
InfoController.h:
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
#interface InfoController : UIViewController <MFMailComposeViewControllerDelegate>
#property (weak, nonatomic) IBOutlet UIButton *emailMeButton;
-(IBAction)emailMe:(id)sender;
#end
InfoController.m
#import "InfoController.h"
#interface InfoController ()
#end
#implementation InfoController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (IBAction)emailMe:(id)sender {
[self dismissViewControllerAnimated:YES completion:^{
[self sendMeMail];
}];
}
- (void)sendMeMail {
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init];
if([MFMailComposeViewController canSendMail]){
if(mailController)
{
NSLog(#"%#", self); // This returns InfoController
mailController.mailComposeDelegate = self;
[mailController setSubject:#"I have an issue"];
[mailController setMessageBody:#"My issue is ...." isHTML:YES];
[self presentViewController:mailController animated:YES completion:nil];
}
}
}
- (void)mailComposeController:(MFMailComposeViewController*)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError*)error;
{
if (result == MFMailComposeResultSent) {
NSLog(#"It's sent!");
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Also, if I comment out the [self dismissViewControllerAnimated:YES completion:^{}]; in (IBAction)emailMe, the MFMailComposeViewController animates in but it is partially hidden behind the partial curl. How can I first dismiss the curl and then animate in the MFMailComposeViewController?
Thanks very much!
Edit: Below image of what the view looks like if I comment out [self dismissViewControllerAnimated:YES completion:^{}];
It's a communications issue between view-controllers resulting out of an unclear parent-child view-controller relationship... Without using a protocol and delegation, this won't work properly.
The rule of thumb is:
Parents know about their children, but children don't need to know about their parents.
(Sounds heartless, but it makes sense, if you think about it).
Translated to ViewController relationships: Presenting view controllers need to know about their child view controllers, but child view controllers must not know about their parent (presenting) view controllers: child view controllers use their delegates to send messages back to their (unknown) parents.
You know that something is wrong if you have to add #Class declarations in your headers to fix chained #import compiler warnings. Cross-references are always a bad thing (btw, that's also the reason why delegates should always be (assign) and never (strong), as this would result in a cross-reference-loop and a group of Zombies)
So, let's look at these relationships for your project:
As you didn't say, I assume the calling controller is named MainController. So we'll have:
A MainController, the parent, owning and presenting the InfoController
An InfoController (revealed partially below MainController), owning and presenting a:
MailComposer, which cannot be presented because it would be displayed below the MainController.
So you want to have this:
A MainController, the parent, owning and presenting the InfoController & MFMailController
An InfoController (revealed partially below MainController)
an "Email-Button" in the InfoController's view. On click it will inform the MainController (it's unknown delegate) that it should dismiss the InfoController (itself) and present the MailComposer
an MailComposer that will be owned (presented & dismissed) by the MainController and not by the InfoController
1. InfoController: Defines a #protocol InfoControllerDelegate:
The child controller defines a protocol and has a delegate of unspecified type which complies to its protocol (in other words: the delegate can be any object, but it must have this one method)
#protocol InfoControllerDelegate
- (void)returnAndSendMail;
#end
#interface InfoControllerDelegate : UIViewController // …
#property (assign) id<InfoControllerDelegate> delegate
// ...
#end
2. MainController owns and creates both InfoController and MFMailController
...and the MainController adopts both the InfoControllerDelegate and the MFMailComposeDelegate protocol, so it can dismiss the MFMailComposer again (Note, that doesn't and probably shouldn't need to be strong properties, just showing this here to make it clear)
#interface MainController <InfoControllerDelegate, MFMailComposeViewControllerDelegate>
#property (strong) InfoController *infoController;
#property (strong) MFMailComposeViewController *mailComposer;
3. MainController presents its InfoViewController and sets itself as the delegate
// however you get the reference to InfoController, just assuming it's there
infoController.delegate = self;
[self presentViewController:infoController animated:YES completion:nil];
The 'infoController.delegate = self' is the crucial step. This gives the infoController a possibility to send a message back to the MainController without knowing it ObjectType (Class). No #import required. All it knows, it that it's an object that has the method -returnAndSendMail; and that's all we need to know.
Typically you would create your viewController with alloc/init and let it load its xib lazily.
Or, if you're working with Storyboards and Segues, you probably want to intercept the segue (in MainController) in order to set the delegate programmatically:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// hook in the segue to set the delegate of the target
if([segue.identifier isEqualToString:#"infoControllerSegue"]) {
InfoController *infoController = (InfoController*)segue.destinationViewController;
infoController.delegate = self;
}
}
4. In InfoController, the eMail button is pressed:
When the eMail button is pressed, the delegate (MainController) is called. Note that it's not relevant that self.delegate is the MainController, it's just relevant that it has this method -returnAndSendMail
- (IBAction)sendEmailButtonPressed:(id)sender {
// this method dismisses ourself and sends an eMail
[self.delegate returnAndSendMail];
}
...and here (in MainController!), you'll dismiss the InfoController (clean up because it's the responsibility of the MainController) and present the MFMailController:
- (void)returnAndSendMail {
// dismiss the InfoController (close revealing page)
[self dismissViewControllerAnimated:YES completion:^{
// and present MFMailController from MainController
self.mailComposer.delegate = self;
[self presentViewController:self.mailComposer animated:YES completion:nil];
}];
}
so, what you're doing with the MFMailController is practically the same as with the InfoController. Both have their unknown delegate, so they can message back and if they do, you can dismiss them and proceed with whatever you should to do.
Notes
-dismissViewControllerAnimated:completion: should not be called from the child view controller. In the docs, it says: "The presenting view controller is responsible for dismissing the view controller it presented.". That's why we still need delegation. And it's useful, because the relationships and responsibilities of parents are important! Indeed. You can't create something and then just leave it be. Well, you can, but you shouldn't.
if you wouldn't use a revealing view controller animation, you could chain these Parent (adopting Child Protocol) - Child (defining protocol for parent and adopting protocol for grandchild) - Grandchild (defining protocol for ...
Again: a design where one MainController is owning and presenting all the child viewController is really a bad design. So the solution presented is about protocols and communication and not about putting everything in one MainController
I don't think that blocks as a coding technology free us from the need to define relationships and declare Protocols
Hope that helps
When you present a viewController, the viewController you are presenting from needs to be in the view hierarchy. The two VC's hold pointers to each other in their properties presentingViewController and presentedViewController, so both controllers need to be in memory.
By dismissing then running presenting code from the being-dismissed view controller, you are breaking this relationship.
Instead, you should be doing the presenting of your mailController from the viewController that presented InfoController, after the infoController has been dismissed.
The trad way to do this is via a delegate callback to the underlying viewController which then handles the two steps of dismissing and presenting. But now we use blocks..
Move your sendMeMail method into the VC that presented infoController
Then - in infoController - you can call it in the completion block…
- (IBAction)emailMe:(id)sender {
UIViewController* presentingVC = self.presentingViewController;
[presentingVC dismissViewControllerAnimated:YES completion:^{
if ([presentingVC respondsToSelector:#selector(sendMeMail)])
[presentingVC performSelector:#selector(sendMeMail)
withObject:nil];
}];
}
(you need to get a local pointer to self.presentingViewController because you cannot refer to that property after the controller has been dismissed)
Alternatively keep all of the code in infoController by putting the sendMeMail code in the completion block:
- (IBAction)emailMe:(id)sender {
UIViewController* presentingVC = self.presentingViewController;
[presentingVC dismissViewControllerAnimated:YES completion:^{
MFMailComposeViewController *mailController =
[[MFMailComposeViewController alloc] init];
if([MFMailComposeViewController canSendMail]){
if(mailController) {
NSLog(#"%#", self); // This returns InfoController
mailController.mailComposeDelegate = presentingVC; //edited
[mailController setSubject:#"I have an issue"];
[mailController setMessageBody:#"My issue is ...." isHTML:YES];
[presentingVC presentViewController:mailController
animated:YES
completion:nil];
}
}
}];
}
update/edit
If you put all of the code in the completion block, you should set mailController's mailComposeDelegate to presentingVC, not self. Then handle the delegate method in the presenting viewController.
update 2
#Auro has provided a detailed solution using a delegate method, and in his comments points out that this best expresses the separation of roles. The traditionalist in me agrees, and I do regard dismissViewController:animated:completion as a kludgy and easily misunderstood piece of API.
Apple's docs have this to say:
Dismissing a Presented View Controller
When it comes time to dismiss a presented view controller, the preferred approach is to let the presenting view controller dismiss it. In other words, whenever possible, the same view controller that presented the view controller should also take responsibility for dismissing it. Although there are several techniques for notifying the presenting view controller that its presented view controller should be dismissed, the preferred technique is delegation. For more information, see “Using Delegation to Communicate with Other Controllers.”
Notice that they don't even mention dismissViewController:animated:completion: here, as if they don't have much respect for their own API.
But delegation seems to be an issue that people often struggle with: and can require a lengthy answer... I think this is one of the reasons Apple pushes blocks so hard. In cases where code only needs to execute in one place, the delegate pattern is often regarded by the uninitiated as an overly complex solution to an apparently simple problem.
I suppose the best answer, if you are learning this stuff, is to implement it both ways. Then you will really get a grip on the design patterns in play.
About this,
- (IBAction)emailMe:(id)sender {
[self dismissViewControllerAnimated:YES completion:^{
[self sendMeMail];
}];
}
After dismissing self viewController, you cannot present view controllers from self.
Then what you can do ?
1) Change the button press method,
- (IBAction)emailMe:(id)sender {
[self sendMeMail];
}
2) You can dismiss the self viewController, when the mailViewController is dismissed.
- (void)mailComposeController:(MFMailComposeViewController*)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError*)error;
{
if (result == MFMailComposeResultSent) {
NSLog(#"It's sent!");
}
[controller dismissViewControllerAnimated:NO completion:^ {
[self dismissViewControllerAnimated:YES completion:nil];
}];
}
If you're using storyboards, try checking the transition types you're using on your segues. You will have issues dismissing layers of viewcontrollers you transition to modally. This may be the source of your problems. Try switching them to push instead. Although you can write delegates to accomplish dismissing viewcontrollers it really shouldn't be needed. This is an overly complicated solution. Image if you have a viewcontroller that transitions to dozens of different storyboards, are you going to have dozens of delegates controlling dismissal? It seems suboptimal.
Use this code
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:composer animated:YES completion:nil];
Instead of
[self presentViewController:picker animated:YES completion:NULL];

DismissmodalViewController donot remove the modal View

I have been figuring out this since yesterday but have not got that correct yet.
I have added the modalviewcontroller for my loading view controller on top of my tab bar controller and it works fine.
Added the code in app Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
[navController.navigationBar setTintColor:[UIColor blackColor]];
[window addSubview:rootController.view];
[window makeKeyAndVisible];
LoadingViewController *lvc = [[LoadingViewController alloc] initWithNibName:#"LoadingView" bundle:nil];
// Delegate added here
lvc.loadingDelegate = self;
[rootController presentModalViewController:lvc animated:YES];
[self URL];
[lvc release];
return TRUE;
}
Now I do my parsing and when its done I call the following code in different view name XMLParsingView.m where the parsing got over.
- (void)handleLoadedApps
{
LoadingViewController *loading = [[[LoadingViewController alloc] init] autorelease];
//delegating to let the load view controller know to dimiss itself by defining disappear method in protocol
[loading.loadingDelegate disappear];
}
and in loading view controller I have method which calls dismissModalViewControlAnimated:
-(void)disappear{
[activity stopAnimating];
[activity removeFromSuperview];
[self removeFromSuperview];
[self dismissModalViewControllerAnimated:YES];
}
But for some reason it will never remove the view and not load it back to my tab bar controller.
Really need help here if any one have come across such issues.
Sagos
In your code you seem to create, without a nib, a new LoadingViewController and immediately go and dismiss it. In your app delegate you create your first loadingViewController with a nib, present it modally on rootController and then release it. Since you want to dismiss it outside your app delegate you have
3 choices, (hardest to fastest and most sane)
a) Key-Value-Observing on a property of XMLParsingView from LoadingViewController to remove itself when the task finishes.
b) Use delegation to inform the LoadingViewController when the task finishes to dismiss itself.
c) Fetch your rootController from your [[UIApplication sharedApplication] delegate], which means you must expose rootController as a property or through a method, and make rootController dismiss your modal.
You need to call dismissModalViewControllerAnimated on the rootViewController, not the loading view controller.

viewWillAppear not invoked when presenting the viewController modally (iOS)

I have an issue, viewWillAppear method in the UIViewController which is added to the screen by presentModalViewController method does not "go into" viewWillAppear method. Only time this method is invoked is together with the viewDidLoad, the first time.
- (IBAction)openModal:(id)sender {
if (self.nvc == nil) {
ModalViewController *vc = [[ModalViewController alloc] init];
self.nvc = vc;
[vc release];
}
self.nvc.segmentedIndex = [[self.navigationController.viewControllers objectAtIndex:0] index];
[self presentModalViewController:self.nvc animated:YES];
}
The property is of type retain.
Thanks in advance.
right, it works different and it now goes into viewWillAppear only once. So if you want to change the appearance of your view after dismissing a modal view you should do it using a modal delegate pattern. See the link:
http://useyourloaf.com/blog/2010/5/3/ipad-modal-view-controllers.html
Though it's for iPad, but you can get the idea. In the latest iOS versions it works the same.
Hope it helps

Call Function in Underlying ViewController as Modal View Controller is Dismissed

I have a mainViewController. I call [self pushModalViewController:someViewController] which makes someViewController the active view.
Now I want to call a function in mainViewController as someViewController disappears with [self dismissModalViewController].
viewDidAppear does not get called probably because the view was already there, just beneath the modal view. How does one go about calling a function in the mainViewController once the modalView dismisses itself?
Thanks a lot!
This answer was rewritten/expanded to explain the 3 most important approaches (#galambalazs)
1. Blocks
The simplest approach is using a callback block. This is good if you only have one listener (the parent view controller) interested in the dismissal. You may even pass some data with the event.
In MainViewController.m
SecondViewController* svc = [[SecondViewController alloc] init];
svc.didDismiss = ^(NSString *data) {
// this method gets called in MainVC when your SecondVC is dismissed
NSLog(#"Dismissed SecondViewController");
};
[self presentViewController:svc animated:YES completion:nil];
In SecondViewController.h
#interface MainViewController : UIViewController
#property (nonatomic, copy) void (^didDismiss)(NSString *data);
// ... other properties
#end
In SecondViewController.m
- (IBAction)close:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
if (self.didDismiss)
self.didDismiss(#"some extra data");
}
2. Delegation
Delegation is the recommended pattern by Apple:
Dismissing a Presented View Controller
If the presented view controller must return data to the presenting view controller, use the delegation design pattern to facilitate the transfer. Delegation makes it easier to reuse view controllers in different parts of your app. With delegation, the presented view controller stores a reference to a delegate object that implements methods from a formal protocol. As it gathers results, the presented view controller calls those methods on its delegate. In a typical implementation, the presenting view controller makes itself the delegate of its presented view controller.
MainViewController
In MainViewController.h
#interface MainViewController : UIViewController <SecondViewControllerDelegate>
- (void)didDismissViewController:(UIViewController*)vc;
// ... properties
#end
Somewhere in MainViewController.m (presenting)
SecondViewController* svc = [[SecondViewController alloc] init];
svc.delegate = self;
[self presentViewController:svc animated:YES completion:nil];
Somewhere else in MainViewController.m (being told about the dismissal)
- (void)didDismissViewController:(UIViewController*)vc
{
// this method gets called in MainVC when your SecondVC is dismissed
NSLog(#"Dismissed SecondViewController");
}
SecondViewController
In SecondViewController.h
#protocol SecondViewControllerDelegate <NSObject>
- (void)didDismissViewController:(UIViewController*)vc;
#end
#interface SecondViewController : UIViewController
#property (nonatomic, weak) id<SecondViewControllerDelegate> delegate;
// ... other properties
#end
Somewhere in SecondViewController.m
[self.delegate myActionFromViewController:self];
[self dismissViewControllerAnimated:YES completion:nil];
(note: the protocol with didDismissViewController: method could be reused throughout your app)
3. Notifications
Another solution is sending an NSNotification. This is a valid approach as well, it might be easier than delegation in case you only want to notify about the dismissal without passing much data. But it's main use case is when you want multiple listeners for the dismissal event (other than just the parent view controller).
But make sure to always remove yourself from NSNotificationCentre after you are done! Otherwise you risk of crashing by being called for notifications even after you are deallocated. [editor's note]
In MainViewController.m
- (IBAction)showSecondViewController:(id)sender
{
SecondViewController *secondVC = [[SecondViewController alloc] init];
[self presentViewController:secondVC animated:YES completion:nil];
// Set self to listen for the message "SecondViewControllerDismissed"
// and run a method when this message is detected
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didDismissSecondViewController)
name:#"SecondViewControllerDismissed"
object:nil];
}
- (void)dealloc
{
// simply unsubscribe from *all* notifications upon being deallocated
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)didDismissSecondViewController
{
// this method gets called in MainVC when your SecondVC is dismissed
NSLog(#"Dismissed SecondViewController");
}
In SecondViewController.m
- (IBAction)close:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
// This sends a message through the NSNotificationCenter
// to any listeners for "SecondViewControllerDismissed"
[[NSNotificationCenter defaultCenter]
postNotificationName:#"SecondViewControllerDismissed"
object:nil userInfo:nil];
}
Hope this helps!
Using exit (unwind) segue
When you're using storyboards and segues you can use a very handy approach with minimal code to dismiss a modal view controller and inform the underlying view controller that the modal view controller has been closed.
Using exit (unwind) segues you gain 3 advantages:
you don't need to write any code to dismiss the modal view controller and
you can have iOS call a callback method inside the underlying view controller that has presented the model view controller.
you use the very same semantics that you already know from implementing prepareForSegue:
Implement it with only 2 steps
Create an action method in the Parent view controller that presents another (modal) view controller:
Swift
#IBAction func unwindFromSegue(segue: UIStoryboardSegue) {
print("Unwind from segue", segue.identifier)
}
Objective-C
- (IBAction)unwindFromSegue:(UIStoryboardSegue *)segue {
NSLog(#"Unwind from segue %s", segue.identifier);
}
In the storyboard, On child view controller right click the exit segue (aka unwind segue, it's the last of the icons at the top of your view controller), drag & drop unwindFromSegue: to your button and select action.
You're done! Now the modal view controller closes when you click the dismiss button and unwindFromSegue: informs your underlying view controller(Parent) that the modal view controller(Child) has closed.
Here's a callback solution which takes less modifications to your modal and parent:
In the Model's .h add:
#property (nonatomic, copy) void (^dismissed)();
In the Model's .m put this in the completion when you dismiss the modal:
[self dismissViewControllerAnimated:YES completion:^{
if(self.dismissed)
self.dismissed();
}];
In the parent view controller when you instantiate your modal set the dismissed callback:
Modal = //Init your modal
[Modal setDismissed:^{
//do stuff you wanted when it's dimissed
}];
[self presentViewController:Modal animated:YES completion:nil];

Can presentModalViewController work at startup?

I'd like to use a modal UITableView at startup to ask users for password, etc. if they are not already configured. However, the command to call the uitableview doesn't seem to work inside viewDidLoad.
startup code:
- (void)viewDidLoad {
rootViewController = [[SettingsController alloc]
initWithStyle:UITableViewStyleGrouped];
navigationController = [[UINavigationController alloc]
initWithRootViewController:rootViewController];
// place where code doesn't work
//[self presentModalViewController:navigationController animated:YES];
}
However, the same code works fine when called later by a button:
- (IBAction)settingsPressed:(id)sender{
[self presentModalViewController:navigationController animated:YES];
}
Related question: how do I sense (at the upper level) when the UITableView has used the command to quit?
[self.parentViewController dismissModalViewControllerAnimated:YES];
You can place the presentModalViewController:animated: call elsewhere in code - it should work in the viewWillAppear method of the view controller, or in the applicationDidFinishLaunching method in the app delegate (this is where I place my on-launch modal controllers).
As for knowing when the view controller disappears, you can define a method on the parent view controller and override the implementation of dismissModalViewControllerAnimated on the child controller to call the method. Something like this:
// Parent view controller, of class ParentController
- (void)modalViewControllerWasDismissed {
NSLog(#"dismissed!");
}
// Modal (child) view controller
- (void)dismissModalViewControllerAnimated:(BOOL)animated {
ParentController *parent = (ParentController *)(self.parentViewController);
[parent modalViewControllerWasDismissed];
[super dismissModalViewControllerAnimated:animated];
}
I had quite the same problem. I know the topic is old but maybe my solution could help someone else...
You just have to move your modal definition in a method:
// ModalViewController initialization
- (void) presentStartUpModal
{
ModalStartupViewController *startUpModal = [[ModalStartupViewController alloc] initWithNibName:#"StartUpModalView" bundle:nil];
startUpModal.delegate = self;
[self presentModalViewController:startUpModal animated:YES];
[startUpModal release];
}
Next, in viewDidLoad, call your modal definition method in a performSelector:withObject:afterDelay: with 0 as delay value. Like this:
- (void)viewDidLoad
{
[super viewDidLoad];
//[self presentStartUpModal]; // <== This line don't seems to work but the next one is fine.
[self performSelector:#selector(presentStartUpModal) withObject:nil afterDelay:0.0];
}
I still don't understand why the 'standard' way doesn't work.
If you are going to do it like that then you are going to have to declare your own protocol to be able to tell when the UITableView dismissed the parentViewController, so you declare a protocol that has a method like
-(void)MyTableViewDidDismiss
then in your parent class you can implement this protocol and after you dismissModalView in tableView you can call MyTableViewDidDismiss on the delegate (whihc is the parent view controller).