Anam
Anam

Reputation: 61

Action sheet doesn't display when the MFMailComposeViewController's cancel button is tapped

I'm trying to incorporate MFMailComposeViewController in my app. When I present it modally, the send button works fine and the email is sent, which implies that the result sent to the delegate is right in that case.

Whereas when I tap the cancel button it hangs up the app. The log shows no errors either, just the screen goes dark and everything gets disabled. Apparently, the result is not being passed to the delegate (I checked it through logs). it appears that the

(void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error

is never called whenever the cancel button is pressed. Probably that's the reason why the actionsheet (Save draft, cancel, delete draft) is not displayed and therefore the app hangs in right there.

I'm using the exact code from Apple's sample apps (MailComposer), it works perfectly there, but somehow fails in mine. :(

Kindly help me if anyone has ever come across the same issue, and successfully resolved it.

My code:

  -(IBAction)emailButtonPressed:(id)sender{

           Class mailClass = (NSClassFromString(@"MFMailComposeViewController"));
       if (mailClass != nil)
          {

          if ([mailClass canSendMail])
            {
              [self displayComposerSheet];
            }
          else
            {
              [self launchMailAppOnDevice];
            }
          }
        else
          {
            [self launchMailAppOnDevice];
          }


}


#pragma mark -
#pragma mark Compose Mail


-(void)displayComposerSheet 
{
    MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
    picker.mailComposeDelegate = self;

    [picker setSubject:@"Ilusiones"];


    // Set up recipients
     NSArray *toRecipients = [NSArray arrayWithObject:@"[email protected]"]; 

     [picker setToRecipients:toRecipients];
     // Attach a screenshot to the email      
     UIGraphicsBeginImageContext(self.view.bounds.size);
     [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
     UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();

         NSData *myData = UIImagePNGRepresentation(viewImage);
     [picker addAttachmentData:myData mimeType:@"image/png" fileName:@"viewImage"];



     // Fill out the email body text
     NSString *emailBody = @"";
     [picker setMessageBody:emailBody isHTML:NO];

     [self presentModalViewController:picker animated:YES];
         [picker release];

 }

 - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error 
 {  

  switch (result)
  {
case MFMailComposeResultCancelled:
    NSLog(@"Result: canceled");
    break;
case MFMailComposeResultSaved:
    NSLog(@"Result: saved");
    break;
case MFMailComposeResultSent:
    NSLog( @"Result: sent");
    break;
case MFMailComposeResultFailed:
    NSLog( @"Result: failed");
    break;
default:
    NSLog(@"Result: not sent");
    break;
 }
 [self dismissModalViewControllerAnimated:YES];
}


#pragma mark -
#pragma mark Workaround


-(void)launchMailAppOnDevice
{
NSString *recipients = @"mailto:[email protected][email protected],[email protected]&subject=illusions!";
NSString *body = @"&body=xyz";

NSString *email = [NSString stringWithFormat:@"%@%@", recipients, body];
email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];
}

Upvotes: 4

Views: 2983

Answers (6)

Bastian
Bastian

Reputation: 4738

Just in case anybody else makes the same mistake as I did... I implemented the mailComposeController:didFinishWithResult:error: method in my Swift code. It worked for a while but after several refactorings I noticed that it was suddenly not being called anymore. Eventually I noticed that the method had taken this form in my code:

private func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
    presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
} 

So, as you can see I was a little bit too eager about privatizing my methods: Since the method is marked @optional in the MFMailComposeViewControllerDelegate you do not have to implement it. And while I actually had implemented it, the private modifier from there on hid the implementation - thus, from outside the file it seemed that the method was actually not implemented, at all! However, since the method was optional, the compiler did not complain...

To conclude: Do not mark optional delegate with the modifier private.

I have been wondering ever since learning Swift why there are no optional methods anymore - now I might have understood ;-).

Upvotes: 0

Jasper
Jasper

Reputation: 7107

Be sure that you use the correct method,

 -(void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error

I had a some what similar method implemented which was never called. I have no idea what I exactly had but the compiler did not give me a warning or error message.

Upvotes: 0

Zeev Vax
Zeev Vax

Reputation: 924

Msoler has it right, teh action sheet is displayed off-screen. The MFMailComposeViewController display the action-sheet at x:y 0:0, so you have to make sure teh calling controller frame is at 0:0. I had a similar issue when I tried to display the MFMailComposeViewController from a view that was embedded in a scroll view.

Upvotes: 1

Chris
Chris

Reputation: 11

I had the same problem,commented out '[picker release];', and it worked fine! Explanation? I have none.

Upvotes: 1

msoler
msoler

Reputation: 2960

Method

(void)mailComposeController:(MFMailComposeViewController*)controller
        didFinishWithResult:(MFMailComposeResult)result
                      error:(NSError*)error

is never called because you don't press any UIActionSheet button after cancelling, as it doesn't show on screen.

The reason this is happening is that the UIActionSheet appears off-screen. If you check the debug log you'll probably see a message saying Presenting action sheet clipped by its superview. Some controls might not respond to touches. On iPhone try -[UIActionSheet showFromTabBar:] or -[UIActionSheet showFromToolbar:] instead of -[UIActionSheet showInView:]."

That's why you see your view getting darker, but no UIActionSheet appears.

In my case, the problem was that my app is universal, but for some reason there was only one MainWindow.xib, and it was larger than the iPhone screen size (it was, in fact, the iPad screen size).

The solution is to create another MainWindow-iPhone.xib with the right dimensions and change the Info.plist entries called Main nib file base (iPad) and Main nib file base (iPhone) so that they point to the right file. Problem solved!

Hope it helps.

Upvotes: 7

Swapna
Swapna

Reputation: 2235

I have implemented the same code . It works absolutely fine. When you click on the delete draft button,didFinishWithResult method is called and the mail is cancelled.

Upvotes: 0

Related Questions