I want to be able to send an email from my app, in which I have got that portion working. The problem is when I cancel sending an email (which is what I'm testing for now) the email part is dismissed but I'm left with a black screen which I can't seem to dismiss.
So this is what I have. I've created a class to handle the email part:
MailViewController.h
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#interface MailViewController : UIViewController <MFMailComposeViewControllerDelegate>
#end
Implemented as follows:
#import "MailViewController.h"
#interface MailViewController ()
#end
#implementation MailViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
if([MFMailComposeViewController canSendMail]){
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
[mailer setMailComposeDelegate:self];
[mailer setSubject:#"Subject"];
NSMutableArray *toArray = [[NSMutableArray alloc] initWithObjects:#"email#gmail.com", nil];
[mailer setToRecipients:toArray];
[self presentViewController:mailer animated:YES completion:nil];
}
else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Failuer" message:#"This device is unable to send emails" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
switch (result){
case MFMailComposeResultCancelled:
NSLog(#"Mail cancelled: you cancelled the operation and no email was sent");
break;
case MFMailComposeResultFailed:
NSLog(#"Mail failed");
break;
case MFMailComposeResultSaved:
NSLog(#"Mail saved");
break;
case MFMailComposeResultSent:
NSLog(#"Mail sent");
break;
default:
NSLog(#"Mail not sent");
break;
}
[controller dismissViewControllerAnimated:YES completion:nil];
}
#end
And all of this is shown from another view like so:
-(IBAction)sendMessage:(id)sender{
NSLog(#"Going to send an email....");
MailViewController *mail = [[MailViewController alloc] init];
[self.navigationController pushViewController:mail animated:YES];
}
So when I run all of this, the email program opens and I can do email stuff. I can select Cancel, then Delete Draft and the email program is removed. However, I'm left with a black screen which I can then select Back from the top navigation bar to return to the previous view.
I simply want the app to return to the view that shows the email program when an email is sent or cancelled (or whatever). I'm sure it's something simple I'm missing.
The problem is tat you are pushing a MailViewController and then dimissing, if you push you should pop the view controller.
MFMailComposeViewControllers should be presented modally, not pushed on the navigation stack. You also dont need a subclass, you can create an instance of MFMailComposeViewController directly:
-(IBAction)sendMessage:(id)sender{
NSLog(#"Going to send an email....");
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
mail.mailComposeDelegate = self;
[self presentViewController:mail animated:YES completion:nil];
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
switch (result){
case MFMailComposeResultCancelled:
NSLog(#"Mail cancelled: you cancelled the operation and no email was sent");
break;
case MFMailComposeResultFailed:
NSLog(#"Mail failed");
break;
case MFMailComposeResultSaved:
NSLog(#"Mail saved");
break;
case MFMailComposeResultSent:
NSLog(#"Mail sent");
break;
default:
NSLog(#"Mail not sent");
break;
}
[controller dismissViewControllerAnimated:YES completion:nil];
}
There's a couple architecture questions I have, but first - the TL;DR version:
[self.navigationController popViewControllerAnimated:NO];
You could also choose YES for the boolean, but judging on your implementation, that wouldn't result in something nice. Put that in the delegate call for completion.
However, why not just have whatever's pushing MailViewController onto the stack handle the job of being the MFMailComposeViewControllerDelegate? You're doing nothing here that requires a completely new view controller be pushed onto the stack.
Related
I'm in a bit of trouble, I've spent half a day working onto this issue I have with no major results after all deprecations in iOS6 and other issues. This iOS app, has a send email option when after pushing a button, the app takes a screenshot of my WebView, attaches it to the email and from there, have the normal options to cancel or send the email and return to the app. I made it to the part where the email pops up, and actually 2 issues here: one is that after pressing either cancel or send, the email view won't dismiss, the app is stuck in the email view. And the second issue I have is the image that gets attached is just a tiny icon (blue with an question mark just like it is not recognised or is missing...
Can someone please point me in the right direction as I feel like I'm going crazy. I've researched the net backwards and forwards with no luck. Many similar threads but different issues not exactly related to my issues unfortunately. Sorry, and thanks in advance.
Here is my code:
// in my LiveView.h file
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import <MessageUI/MessageUI.h>
#import "CamSetup.h"
#interface LiveView : UIViewController < MFMailComposeViewControllerDelegate , ADBannerViewDelegate >
....
-(IBAction)shotAndSend:(id)sender;
// in my LiveView.m file:
- (void)mailComposer:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
NSLog(#"in didFinishWithResult:");
switch (result)
{
case MFMailComposeResultCancelled:
NSLog(#"cancelled");
break;
case MFMailComposeResultSaved:
NSLog(#"saved");
break;
case MFMailComposeResultSent:
NSLog(#"sent");
break;
case MFMailComposeResultFailed: {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Error sending mail",#"Error sending mail")
message:[error localizedDescription] delegate:nil
cancelButtonTitle:NSLocalizedString(#"test",#"test")
otherButtonTitles:nil];
[alert show];
break;
}
default: break;
}
[self dismissViewControllerAnimated:NO completion:Nil];
}
-(IBAction)shotAndSend:(id)sender
{
UIGraphicsBeginImageContext(_myWebView.frame.size);
[_myWebView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * imageData = UIImageJPEGRepresentation (image, 2.1);
if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController * mailComposer = [[MFMailComposeViewController alloc] init];
mailComposer.mailComposeDelegate = self;
[mailComposer addAttachmentData:imageData mimeType:#"image/jpeg" fileName:#"attachment.jpeg"];
[mailComposer setSubject:#"A screenshot from my App"];
[mailComposer setToRecipients:[NSArray arrayWithObjects:#"123#yexample.com", nil]];
[self presentViewController: mailComposer animated:YES completion:NULL];
}
}
Your code isn't working because you have the wrong method. You have:
- (void)mailComposer:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
but it should be:
- (void)mailComposeController:(MFMailComposeViewController*)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
You need a delegate of your view that will dismiss that view for you. The fact that you don't see the log indicate you that you haven't implemented correctly the delegation protocol. Take a look at some example in the apple documentation.
I have a button in my app, when press it, a MFMailComposerViewController will show up, and when the composer dismisses, a MBProgressHUD with a custom view will show to tell user whether the mail is sent successfully or not.
It works fine if I press send button in the composer, the mail will be sent, the composer will be dismissed and a HUD will show. However, if I press the cancel button in the composer view, the composer dismisses, but HUD doesn't show, and the app crashes.
Here are the Log of crash.
2012-02-02 22:49:34.821 App[5091:707] -[ViewController size]: unrecognized selector
sent to instance 0x319210
2012-02-02 22:49:34.831 App[5091:707] *** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[ViewController size]: unrecognized selector sent to instance 0x319210'
*** First throw call stack:
(0x340af8bf 0x342ff1e5 0x340b2acb 0x340b1945 0x340b27b8
0x3748cfa5 0xf051 0x203d1 0x37553f5b 0x374f393b 0x374f37bf
0x3746d81b 0x37472fb9 0x34bc4ba7 0x36ce3e8d 0x340822dd
0x340054dd 0x340053a5 0x30889fcd 0x37486743 0xe7a7 0xe74c)
terminate called throwing an exception
The ViewController is the controller that present the mail composer.
Here are some code I used:
-(void)showHUDWithMessage:(NSString *)msg
{
HUD = [[MBProgressHUD alloc]initWithWindow:self.window];
[self.window addSubview:HUD];
HUD.delegate = self;
UIImage *image;
NSString *labelTextToShow;
//Do something here
UIImageView *imageView = [[[UIImageView alloc]initWithImage:image]autorelease];
HUD.labelText = labelTextToShow;
HUD.customView = imageView;
HUD.mode = MBProgressHUDModeCustomView;
[HUD show:YES];
[HUD hide:YES afterDelay:3.0];
}
-(void)mailFriend:(id)sender
{
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc]init];
mailController.mailComposeDelegate = self;
[mailController setSubject:#"Mail Subject"];
NSString *emailBody = #"Message";
[mailController setMessageBody:emailBody isHTML:YES];
[self presentModalViewController:mailController animated:YES];
}
-(void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
NSString *msg;
switch (result) {
case MFMailComposeResultSent:
msg = #"Sent";
break;
case MFMailComposeResultFailed:
msg = #"Fail";
break;
case MFMailComposeResultCancelled:
msg = #"Cancelled";
break;
case MFMailComposeResultSaved:
msg = #"Cancelled";
break;
default:
break;
}
//Show HUD here
[self showHUDWithMessage:msg];
[self dismissModalViewControllerAnimated:YES];
[controller release];
}
Since if the mail is sent, the composer view can dismiss successfully, the HUD can also show correctly, I really don't know what's wrong here...
Thanks!
Most likely this is overreleasing memory issue. Enable NSZombie and test your app.
Couple of things:
Where have you created controller ? You are responsible to release objects that you own. You don't own controller object so don't release it:
[controller release]; // comment this line
in
-(void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
Next:
-(void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
NSString *msg = nil; // If result is not equal to any of the case statements then you want to pass nil to [self showHUDWithMessage:msg];
switch (result) {
case MFMailComposeResultSent:
msg = #"Sent";
break;
case MFMailComposeResultFailed:
msg = #"Fail";
break;
case MFMailComposeResultCancelled:
msg = #"Cancelled";
break;
case MFMailComposeResultSaved:
msg = #"Cancelled";
break;
default:
break;
}
//Show HUD here
[self showHUDWithMessage:msg];
[self dismissModalViewControllerAnimated:YES];
[controller release];
}
and MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc]init]; in -(void)mailFriend:(id)sender autorelease or release it later. Although this has nothing to do with issue you might have.
Remove all reference to MBProgressHUD and put a simple NSLog in your method. I'll bet my bottom dollar MB is causing this.
If not, memory management is my obvious second choice here. Comment out [controller release];
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Action sheet doesn't display when the MFMailComposeViewController's cancel button is tapped
I've implemented standard mail functionality in my app according to the code sample provided by Apple.
I'm setting up the delegate as follows:
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
and I'm implementing
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
Hitting the Send button invokes the delegate and it all works fine. However, hitting the Cancel button doesn't call the delegate and it just dims the view; the app hangs right there.
After reading similar threads here, I've been thinking that the view could be off-screen for some reason which is beyond my comprehension at this point. Note that the view is being created programmatically and is not using a xib file.
Any thoughts or ideas ?
You need to implement mailComposeController:didFinishWithResult:error delegate. And in that you dismiss the view which is showing your mail view. If you have opened the mail view as a modalView, then the way to dismiss this is -
- (void)mailComposeController:(MFMailComposeViewController*)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError*)error
{
if(error) NSLog(#"ERROR - mailComposeController: %#", [error localizedDescription]);
[self dismissModalViewControllerAnimated:YES];
return;
}
It may be helpful to you
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
// Notifies users about errors associated with the interface
switch (result)
{
case MFMailComposeResultCancelled:
//NSLog(#"Result: canceled");
break;
case MFMailComposeResultSaved:
//NSLog(#"Result: saved");
break;
case MFMailComposeResultSent:
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Result" message:#"Mail Sent Successfully" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
break;
case MFMailComposeResultFailed:
//NSLog(#"Result: failed");
break;
default:
//NSLog(#"Result: not sent");
break;
}
[self dismissModalViewControllerAnimated:YES];
}
Try to add even simple delegate :
[picker setDelegate:self];
After though that after using MFMailComposeViewController the move to MFMessageComposeViewController was straight foward, but there is a catch.
Suppose this code:
MFMessageComposeViewController* mySMS = [[MFMessageComposeViewController alloc] init];
[mySMS setDelegate:self];
[self presentModalViewController:mySMS animated:YES];
It works this way for mails, but in sms you should set different the delegate to an internal structure like this:
[SMS setMessageComposeDelegate:self];
Hope you don not get stuck on this as I did early today.
You need to Implement the delegate method -(void)mailComposeController(MFMailComposeViewController*)controller didFinishWithResult (MFMailComposeResult)result error:(NSError*)error:
And inside it you should dismiss it yourself:
-(void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
[self dismissModalViewControllerAnimated:YES];
}
You can see MFMailComposeResult in Apple documentation
enum MFMailComposeResult {
MFMailComposeResultCancelled,
MFMailComposeResultSaved,
MFMailComposeResultSent,
MFMailComposeResultFailed
};
typedef enum MFMailComposeResult MFMailComposeResult;
And you must dismiss controller by yourself in delegate method
- (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
switch (result){
case MFMailComposeResultCancelled:
NSLog(#"Mail cancelled");
break;
case MFMailComposeResultSaved:
NSLog(#"Mail saved");
break;
case MFMailComposeResultSent:
NSLog(#"Mail sent");
break;
case MFMailComposeResultFailed:
NSLog(#"Mail sent failure: %#", [error localizedDescription]);
break;
default:
break;
}
// Close the Mail Interface
[self dismissViewControllerAnimated:YES completion:NULL];
}
I am sending an email from my iPhone app using MFMailComposeViewController. This works fine but after sending or canceling I need to dismiss the modalViewController. When I do this I get a Program received signal: “EXC_BAD_ACCESS”. This is not very descriptive... Please help!!
This is the code for creating the mail and the modalViewController
-(void)sendFavMail:(NSString *)body{
MFMailComposeViewController* mailViewController = [[MFMailComposeViewController alloc] init];
mailViewController.mailComposeDelegate = self;
[mailViewController setSubject:#"Favorites List"];
[mailViewController setMessageBody:body isHTML:YES];
[self presentModalViewController:mailViewController animated:YES];
[mailViewController release];
}
And this is the code for the delegate, dismissing the modalviewcontroller:
- (void)mailComposeController:(MFMailComposeViewController*)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError*)error;
{
switch (result)
{
case MFMailComposeResultCancelled:
NSLog(#"Cancelled sending");
break;
case MFMailComposeResultSaved:
NSLog(#"Message Saved");
break;
case MFMailComposeResultSent:
NSLog(#"Message Sent");
break;
case MFMailComposeResultFailed:
NSLog(#"Sending Failed");
break;
default:
NSLog(#"Message not sent");
break;
}
[self dismissModalViewControllerAnimated:YES];
}
Thanks for your help!!
Darn, fixed it myself :-)
I released an object in the body of the message before sending/cancelling. What I did to fix it is to declare this body object autoreleased. And what do you know? IT WORKS!
Just answered my own question...