I'm calling a method from viewcontroller in an appdelegate. When I was testing functionality I just used NSLog message which works fine (so the connection between viewcontroller and appdelegate is OK). The problem appears once I add a email form into this method. The message I receive is:
Warning: Attempt to present <MFMailComposeViewController: 0x1fdc3990> on <ViewController: 0x1fd9e3b0> whose view is not in the window hierarchy!
Anyone who know what to do? I know there are lot of topics with 'whose view is not in the window hierarchy' issue but none of them helped me.
ViewController.m
...
-(void)mail{
NSLog(#"blablabla");
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
mail.mailComposeDelegate = self;
[mail setSubject:#"Hello and welcome!"];
NSArray *toRecipients = [NSArray arrayWithObject:#"tomas.javnicky#gmail.com"];
[mail setToRecipients:toRecipients];
[mail setCcRecipients:toRecipients];
NSString *emailBody = #"Hey all!";
[mail setMessageBody:emailBody isHTML:NO];
mail.modalPresentationStyle = UIModalPresentationPageSheet;
[self presentViewController:mail animated:YES completion:nil];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error!"
message:#"E-mail is not supported on your device"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
switch (result) {
case MFMailComposeResultCancelled:
break;
case MFMailComposeResultSaved:
NSLog(#"mail saved");
break;
case MFMailComposeResultSent:
NSLog(#"mail sent");
break;
case MFMailComposeResultFailed:
NSLog(#"mail failed");
break;
}
[self dismissViewControllerAnimated:YES
completion:nil];
}
...
Appdelegate.m
...
-(void)something {
ViewController * vc = [[ViewController alloc] init];
[vc mail];
}
...
This is what solved my problem:
- (void)something {
ViewController *rootViewController = (ViewController*)self.window.rootViewController;
[rootViewController mail];
}
Also check the answer by rmaddy for more info.
Now that you've posted code, the problem is obvious. You create a view controller but you never display it (in the something method). Then you call a method on this undisplayed view controller (mail) which attempts to display the mail view controller from the undisplayed view controller. This is what causes the error.
You need to display the mail controller from a view controller that is already being displayed (such as the rootViewController maybe).
What is the point of your ViewController class?
Related
I am using MFMailComposeViewController in my application to compose a feedback E-Mail. The MFMailComposeViewController gets displayed, but can't be closed.
Method used to open the MFMailComposeViewController modal window:
-(IBAction) feedbackBtnClicked:(id)sender {
// Dismiss the Old View Controller
[self dismissViewControllerAnimated:NO completion:NULL];
// Present the New View Controller
if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
mail.mailComposeDelegate = self;
[mail setSubject:#"Sample Subject"];
[mail setMessageBody:#"Here is some main text in the email!" isHTML:NO];
[mail setToRecipients:#[#"example#mail.com"]];
[self presentViewController:mail animated:YES completion:NULL];
}
else
{
NSLog(#"This device cannot send email");
}
}
Here is what happens, when clicking on the buttons:
Senden (Send) - The E-Mail gets sent, but the modal window stays open; clicking on that button multiple times results in sending multiple E-Mails without the modal window ever getting closed.
Abbrechen (Cancel) - Nothing happens
How to dismiss make sure the MFMailComposeViewController gets dismissed after clicking on those buttons?
You need to implement the MFMailComposeViewControllerDelegate method mailComposeController:didFinishWithResult:error:, and dismiss the mail view controller…
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller
didFinishWithResult:(MessageComposeResult)result
{
[self dismissViewControllerAnimated:YES completion:NULL];
}
I am experiencing a strange issue with the MFMailComposeViewController. I have been working with the MFMailComposeViewController in the past without any problems but now I can't figure out what's wrong this time and therefore I would like your help.
In my application I have multiple UIViewControllers where the one named MainViewController is a ViewController container where i implemented a left slide menu. In the MainViewController i added a UINavigationController as well. The UINavigationController is allocated and afterwards added to MainViewController like this:
[self addChildViewController:self.navigationController];
[self.centerView addSubview:self.navigationController.view];
[self.navigationController didMoveToParentViewController:self];
The above work perfectly fine and I can use the navigation controller to push and pop other UIViewControllers.
In the left menu I got different buttons, and one of them is named "About". When it is pressed the following code is executed:
NSArray *array = [NSArray arrayWithObject:[AboutViewController sharedViewController]];
[self.navigationController setViewControllers:array animated:NO];
In AboutViewController i got some buttons where one of them is "Send feedback". The "Send feedback" button executes the following code:
if([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
NSArray *toRecipients = [NSArray arrayWithObjects:[NSString stringWithFormat:#"feedback#test.com"], nil];
[mailer setToRecipients:toRecipients];
[mailer setSubject:[NSString stringWithFormat:#"Feedback"]];
[self presentViewController:mailer animated:YES completion:nil];
}
Which is also working. The AboutViewController implements the MFMailComposeViewControllerDelegate and its method:
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
switch (result) {
case MFMailComposeResultCancelled:
//NSLog(#"Mail cancelled: you cancelled the operation and no email message was queued.");
break;
case MFMailComposeResultSaved:
//NSLog(#"Mail saved: you saved the email message in the drafts folder.");
break;
case MFMailComposeResultSent:
//NSLog(#"Mail send: the email message is queued in the outbox. It is ready to send.");
break;
case MFMailComposeResultFailed:
//NSLog(#"Mail failed: the email message was not saved or queued, possibly due to an error.");
break;
default:
//NSLog(#"Mail not sent.");
break;
}
// Remove the mail view
[self dismissViewControllerAnimated:YES completion:nil];
}
And its here I get the headache. I call the
[self dismissViewControllerAnimated:YES completion:nil];
as I did in previous apps where it worked without any problems. But in this case the MFMailComposeViewController is not being dismissed and the "Cancel" and "Send" buttons become inactive. I tried to implement a NSLog in the dismiss completion block but it is never being called.
I googled for hours now and can't find a way to solve this problem. I hope you can help me solve this strange issue. If you need more information just ask.
Thanks in advance.
- Sebastian
You have to open mail controller into parent viewcontroller. To do this you have to use delegate.
Call The MFMailComposeViewController in your parentclass and passed value by custom delegate
I managed to solve it. I found out that one of the threads that the MainViewController managed started an endless loop in main thread when the MainViewController should become visible.
I am using QLPreviewController for showing the pdf. And I am sending pdf by tapping the share button and then tap on email in the QLPreviewController.
But i don't know how to get that share button method in QLPreviewController to validate that the email account is available or not. Here is the screenshot:
Please let me know about this.
Thanks in advance.
if you have UInavigationController then you can add a bar button for sharing the content. Or you can add a share button in your viewcontroler.
Use MessageUI. Add the delegates MFMessageComposeViewControllerDelegate, MFMailComposeViewControllerDelegate.
- (IBAction)contactBtn:(id)sender {
if ([MFMailComposeViewController canSendMail]){
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
[controller setSubject:#"Subject"];
[controller setMessageBody:#" " isHTML:NO];
[controller setToRecipients:[NSArray arrayWithObjects:#"a#wa.com",nil]];
controller.mailComposeDelegate = self;
[self presentViewController:controller animated:YES completion:NULL];
}
else{
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:#"Failed!" message:#"Mail can not be sent. Check your email settings." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles: nil] ;
[alert show];
}
}
- (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
[self dismissViewControllerAnimated:YES completion:NULL];
}
The QL previewer is working on separate process. Something like a different app. So there is no way to customize the share button. see details here
This may help you. See this answer.
I have a UIAlertView that launches an email and a messages screen. When a user clicks on the Alert's button, both views open, however, they do not close.
I have a tried adding:
-(void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
[self dismissModalViewControllerAnimated:YES];
}
to the body of the class, however, it did not help.
Here is how the email is presented:
[viewController presentViewController:email animated:YES completion:nil];
Edit Here is the entire code I am using to present the email:
//send email...
-(void)sendEmail{
//mail composer
Class mailClass = (NSClassFromString(#"MFMailComposeViewController"));
if(mailClass != nil){
if([mailClass canSendMail]){
//get the current view controller from the App Delegate
apptester_appDelegate *appDelegate = (apptester_appDelegate *)[[UIApplication sharedApplication] delegate];
UIViewController *viewController = [appDelegate getViewController];
MFMailComposeViewController *email = [[MFMailComposeViewController alloc] init];
email.mailComposeDelegate = self;
//navigation bar color depends on iOS7 or lower...
if(floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1){
[[email navigationBar] setTintColor:[UIColor blackColor];
}else{
[[email navigationBar] setBarTintColor:[UIColor blackColor]];
}
//show the model view...
[viewController presentViewController:email animated:YES completion:nil];
}
}
}
Has anyone else experienced this error?
This may not be relevant, but this app has a tab bar.
Make sure you have set delegate for mail controller
mail.mailComposeDelegate = viewController;
Also try this as well,
[viewController.tabBarController presentViewController:email animated:YES completion:nil];
Have you try this method and pass "controller" instead of self:
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
[controller dismissViewControllerAnimated:YES completion:nil];
}
Thanks!
It is the responsibility of presenting controller to dismiss the modal view. Make sure you implement the delegate of the modal in the presenting controller.
Two things:
1) Make sure you set the mailComposeDelegate on the MFMailComposeViewController before you present it.
2) In your mailComposeController:didFinishWithResult:error: method, you should do:
[controller dismissModalViewControllerAnimated:YES];
My app has a common class that displays an actionSheet whenever "Contact Us" is clicked from any one of the many NIBs.
If the user chooses "Email Us" from the actionSheet popup, I'd like to call the email methods from the same common class. After researching I implemented this:
-(void)SendEmail {
rootViewController = (UIViewController*)
[(AppDelegate*)[[UIApplication sharedApplication] delegate] viewController];
// compose
MFMailComposeViewController* controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = rootViewController;
//format message
NSArray *recipientsArray = [[NSArray alloc] initWithObjects:#"support#somename.com", nil];
[controller setToRecipients:recipientsArray];
[controller setSubject:[NSString stringWithFormat:#"A question about %#",string]];
[controller setMessageBody:outputMutString isHTML:YES];
//send
if (controller) [rootViewController presentModalViewController:controller animated:YES];
}
//didFinishWithResult
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error;{
if (result == MFMailComposeResultSent) {
}
[rootViewController dismissModalViewControllerAnimated:YES];
}
This will launch a new email, however:
The didFinishWithResult doesn't work as the modal view is not removed either after sending the email or pressing the Cancel button
I am getting this warning: assigning to 'id' from incompatible type 'UIViewController *__strong'
controller.mailComposeDelegate = rootViewController;
Help appreciated.
You need to set your rootViewController as adopting the delegate MFMailComposeViewControllerDelegate.
i.e. in your RootViewController.h file, add that part to the interface declaration so that it looks similar to:
#interface RootViewController : UIViewController <MFMailComposeViewControllerDelegate>