I am able to successfully dismiss my MFMailComposeViewController in the didFinishWithResult delegate method. However, I have a scenario where I would like to dismiss the composer without user interaction, like selecting cancel or sending the mail.
I have looked in apple docs and was unable to find anything entirely useful. I have tried calling dismissViewControllerAnimated but that only seems to be working when I am inside the didFinishWithResult delegate method. Is there anyway to force that delegate method or dismiss the composer alternatively?
Assuming you are presenting your mail controller from a UIViewController, you may dismiss it programmatically by calling the UIViewController method:
dismissViewControllerAnimated:completion:
See this apple reference: dismissViewControllerAnimated:completion:
You did mention:
I have tried calling dismissViewControllerAnimated but that only seems
to be working when I am inside the didFinishWithResult delegate method
What you are experiencing may be indicative of a different problem as I was able to successfully do this outside of the mailComposeController:didFinishWithResult:error: delegate method.
Example:
-(void)showMail
{
MFMailComposeViewController *mailController = [[[MFMailComposeViewController alloc] init] autorelease];
//Set the message, subject, etc...
//Display
[someViewController presentViewController:mailController animated:YES completion:nil];
//As a proof of concept, close programmatically after a couple of seconds...
[self performSelector:#selector(dismissMailController) withObject:nil afterDelay:2.0];
}
-(void)dismissMailController
{
[someViewController dismissViewControllerAnimated:YES completion:nil];
}
Related
I'm trying to present a custom UIViewController with one method and dismiss the same UIViewController from another method.
The method being used to present the controller:
-(void)presentViewController {
self.customView = [[UIViewController alloc] init];
[self.customView setModalPresentationStyle: UIModalPresentationOverFullScreen];
[self presentViewController:self.customView animated:YES completion:nil];
}
The method being used to dismiss the controller:
-(void)dismissViewController {
[self.customView dismissViewControllerAnimated:YES completion:nil];
}
When creating a button within my customView, that calls dismissViewController, nothing happens. I know the method is being called because the completion: tag is working, yet nothing happens visually. No errors are being printed to console either.
Maybe I'm doing the whole thing wrong but any help is appreciated! Just trying to learn.
I'm showing an alert like this:
self.connectingAlert = [UIAlertController
alertControllerWithTitle:#""
message:NSLocalizedString(#"CONNECTING", nil)
preferredStyle:UIAlertControllerStyleAlert];
[self.connectingAlert addAction:[UIAlertAction
actionWithTitle:NSLocalizedString(#"BUTTON_CANCEL", nil)
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
[self cancelRequest];
}]];
[self presentViewController:self.connectingAlert animated:TRUE completion:nil];
Then later I want to dismiss the alert programmatically like this:
[self dismissViewControllerAnimated:FALSE completion:nil];
The dismiss code works fine in iOS, but does nothing in Mac Catalyst. This might have something to do with the fact that alerts are presented as a part of the app window, sort of outside of the app, and the presentation style is ignored. But I would expect the dismiss method to still affect the Mac alerts.
I tried this to make sure everything is hooked up correctly:
UIViewController *test1 = self.connectingAlert.presentingViewController;
UIViewController *test2 = self.connectingAlert.presentingViewController.presentedViewController;
test1 returns the navigation controller that the view controller is part of, which seems odd, but it does the same thing on iOS. test2 returns my alert. Just to make sure, I tried this code, but it doesn't work either:
[self.connectingAlert.presentingViewController dismissViewControllerAnimated:FALSE completion:nil];
Does anyone have experience with this? I don't see anything about it in the documentation.
It turns out that while you're supposed to send the dismiss message to the presenting (parent) view controller...
[self dismissViewControllerAnimated:FALSE completion:nil];
...I have to send the dismiss message to the alert instead, and then it dismisses:
[self.connectingAlert dismissViewControllerAnimated:FALSE completion:nil];
The documentation for dismissViewControllerAnimated says:
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, UIKit asks the presenting view controller to handle
the dismissal.
I think what this means is that Mac Catalyst is doing something funny with the connection between the presenting and the presented view controller. If I check self.presentedViewController, that gives me the UIAlertController. But if I call self dismiss dismissViewControllerAnimated..., nothing happens, as if there is no presented view controller. But if I call self.connectingAlert dismissViewControllerAnimated..., the dismiss method somehow finds its way to the real presenting view controller. I'll report this as a bug to Apple.
Meanwhile, I'm happy to have a workaround.
In using ABPeoplePickerNavigationController, didSelecPerson delegate will get called when user selects a person and then the ABPeoplePickerNavigationController dismisses itself. I need to present another view controller when receiving didSelectPerson call. The issue is ABPeoplePickerNavigationController's animation dismissing at this stage is not complete. So, the presentation of the next view controller will generate an error.
If I subclass ABPeoplePickerNavigationController and override:
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[super dismissViewControllerAnimated:flag completion:completion];
[self.myDelegate didDismissPickerCompletion];
}
That will solve the problem. But the Apple doc did state "The ABPeoplePickerNavigationController class does not support subclassing". Does that mean they will reject any attempt to subclass such stated class?
note: I do not wish to use any artificial time delay as a solution.
and then the ABPeoplePickerNavigationController dismisses itself
There's your problem. It is dismissing itself only because you did not dismiss it. Dismiss it, explicitly, yourself! Call dismissViewControllerAnimated:completion: right there in your didSelectPerson: implemention.
Now you have a completion handler, and so you can run code when the dismissal has finished.
I am doing Preferences feature with UIModalPresentationFormSheet with following code in iPad.
self.preferencesViewController = [[PreferenceViewController alloc] initWithNibName:#"PreferenceViewController" bundle:nil];
self.preferencesViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:self.preferencesViewController animated:YES];
When i save preferences and dismiss view controller with
[self dismissModalViewControllerAnimated:YES];
But my preferences is not change.
i write my check preferences coding in viewWillAppear , however viewWillAppear event is not work with UIModalPresentationFormSheet.
I am fine when i use with
self.preferencesViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:self.preferencesViewController animated:YES completion:nil];
Where can i check event for UIModalPresentationFormSheet?
Thanks for your help.
The standard way to get information back to a controller that created you, is to use a delegate protocol. Your preferenceViewController should define a delegate protocol, and the presenting controller should set itself as the delegate before it presents the preferences controller. When you initiate the save of the preferences, PreferenceViewController should send it's delegate a message saying that it's finished and should be dismissed. In the implementation of that delegate method in the presenting controller, it can then read the preferences from where you saved them, and dismiss the PreferenceViewController.
I recently encountered a hair-pulling situation in my iOS app, where I was trying to successively dismiss one presented UIViewController from my window's rootViewController, using:
[rootViewController dismissViewControllerAnimated:YES completion:NULL]
and present another one shortly thereafter (in another method, incidentally), with:
UIViewController *vc2 = [[[MyViewController2 alloc] initWithNibName:nil bundle:nil] autorelease];
[rootViewController presentViewController:vc2 animated:YES completion:NULL];
Problem was, I could never get the second view controller to show up. Turns out, as near as I can tell, dismissViewControllerAnimated:completion: needs that asynchronous block of "completion" time to pass, before presentViewController:animated:completion: will work properly again. This fact is not directly documented in Apple's docs, from what I can tell.
The solution I came up with was to wrap the dismissal with a method that specifies the selector you would want to call afterwards, like so:
- (void)dismissViewController:(UIViewController *)presentingController
postAction:(SEL)postDismissalAction
{
[presentingController dismissViewControllerAnimated:YES
completion:^{
[self performSelectorOnMainThread:postDismissalAction
withObject:nil
waitUntilDone:NO];
}];
}
And then I would call:
[self dismissViewController:self.window.rootViewController
postAction:#selector(methodForNextModalPresentation)];
Anyway, I wanted to post, as I looked around and hadn't seen anyone with this particular problem, so I thought it might be useful for people to understand. And also, I wanted to verify that I'm not hacking a solution that has a better design pattern for resolution.
Just for the sake of clarity. are you saying that this code doesn't work?
[myRootViewController dismissViewControllerAnimated:YES completion:^{
[myRootViewController pushViewController:newController animated:YES];
}];