Iam presenting MFMailComposeViewController from my custom class(not a viewController). In iOS5 it is working fine but in iOS6 it is getting crash immediately after presenting the compose sheet. I found the issue the dealloc method is getting called after presenting the view, so self is deallocating. Due to this mailcomposer cannot call the delegate method on self so it is crashing. I didnt get a solution for that. Am using ARC. How to prevent self from deallocating until the delegate method is getting called?
-(void)shareOnViewController:(UIViewController *)viewController completion:(ShareCompletionHandler)completion
{
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
mailer.modalPresentationStyle = UIModalPresentationPageSheet;
[mailer setSubject:[self.userInfo objectForKey:#"title"]];
NSData *imageData = UIImagePNGRepresentation([self.userInfo objectForKey:#"image"]);
if (imageData) {
[mailer addAttachmentData:imageData mimeType:#"image/png" fileName:#"AttachedImage"];
}
NSURL *emailBody = [self.userInfo objectForKey:#"url"];
if (![emailBody isEqual:#""]) {
[mailer setMessageBody:[emailBody absoluteString] isHTML:NO];
}
[viewController presentModalViewController:mailer animated:YES];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Unable to send mail"
message:#"Device is not configured to send mail"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
self.completionHandler = completion;
}
According to me , The presentModalViewController method is deprecated in iOS 6.0 .
Instead you need to use
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion
Else can you show the crash log ??
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc]
init];
NSData *imageData = UIImagePNGRepresentation(image);
mailComposer.mailComposeDelegate = self;
[mailComposer setSubject:subject];
NSArray * recipents = [NSArray arrayWithObjects:[NSString stringWithFormat:#"%#",NSLocalizedString(#"client_email", #"")],nil];
[mailComposer setToRecipients:recipents];
[mailComposer addAttachmentData:imageData mimeType:#"image/png" fileName:[NSString stringWithFormat:#"imageProfile-%#-%#",someId,lastname]];
mailComposer.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[vc presentViewController:mailComposer animated:YES completion:nil];
}
else
{
UIAlertView *tmp = [[UIAlertView alloc]
initWithTitle:#"Email Account Not Found"
message:#"You need to setup an Email Account!"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[tmp show];
}
One of the possible root cause is ARC.
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer will be auto-released as soon as the method body is fully executed.
You could create a reference to hold the object to avoid ARC releasing it before it is ready.
#property (nonatomic, strong) MFMailComposeViewController * composer;
....
self.composer = [[MFMailComposeViewController alloc] init];
...
[viewController presentViewController:self.composer animated:YES];
[Edited suggestion]
Sorry, I miss out the first part of your question where you were calling the method from another custom class.
I came across the similar situation also. In the end, I converted the "Custom Class" into a Singleton class.
#pragma mark Singleton Methods
+ (id)sharedInstance {
static CustomClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
This will make sure the class will be retained nicely. But this will depend on the usage and design of the CustomClass. Just to share what I did.
[[CustomClass sharedInstance] shareOnViewController:vc completion:...];
Related
I try to integrate the MFMessageComposeViewController in a custom native extension.
The controller works perfectly except for one thing... I can't cancel or dismiss the controller when I hit the cancel button or when the message is send.
There is a problem with the delegate that I don't know how to fix.
Header
#interface platools : UIViewController <MFMessageComposeViewControllerDelegate>
Function call
bool shareIMessage(const char *url)
{
if(![MFMessageComposeViewController canSendText])
{
UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Your device doesn't support SMS!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[warningAlert show];
return false;
}
NSLog(#"shareIMessage");
NSString *ImageStrUrl = [NSString stringWithFormat:#"%s",url];
NSLog(#"%#",ImageStrUrl);
NSURL *ImageURL = [NSURL URLWithString:ImageStrUrl];
NSData *imageData = [NSData dataWithContentsOfURL:ImageURL];
UIViewController *root = [[[UIApplication sharedApplication] keyWindow] rootViewController];
MFMessageComposeViewController *messageController = [[MFMessageComposeViewController alloc] init];
messageController.messageComposeDelegate = (id<MFMessageComposeViewControllerDelegate>)[[UIApplication sharedApplication]delegate];
if ([MFMessageComposeViewController canSendAttachments])
{
NSLog(#"Attachments Can Be Sent.");
BOOL didAttachImage = [messageController addAttachmentData:imageData typeIdentifier:#"public.data" filename:#"image.gif"];
if (didAttachImage)
{
NSLog(#"Image Attached.");
}
else
{
NSLog(#"Image Could Not Be Attached.");
}
}
[root presentViewController:messageController animated:YES completion:nil];
return true;
}
Function delegate
void messageComposeViewController(MFMessageComposeViewController* controller)
{
NSLog(#"messageComposeViewController");
UIViewController *root = [[[UIApplication sharedApplication] keyWindow] rootViewController];
// Close the Mail Interface
[root dismissViewControllerAnimated:YES completion:nil];
}
void didFinishWithResult(MessageComposeResult result)
{
NSLog(#"didFinishWithResult");
}
Full code : http://pastebin.com/tuJY0mwv (the target function is sendIMessage)
In my app, I have a tableView whose material is from my NSmutableDictionary. When I push a button, I want to send these material to a email.Here is the code for sending email:
//Below to send email
- (IBAction)showEmail:(id)sender {
// Email Subject
NSString *emailTitle = #"Course Planner of iBcChem";
// Email Content
NSString *messageBody = #"Please check my course plan";
// To address
NSArray *toRecipents = [NSArray arrayWithObject:#"xxxxx#gmail.com"];
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setSubject:emailTitle];
[mc setMessageBody:messageBody isHTML:NO];//want to fix here
[mc setToRecipients:toRecipents];
// Present mail view controller on screen
[self presentViewController:mc animated:YES completion:NULL];
}
Here is my question:
How can I add my material to the messageBody please? Can I use the method similar to show my dictionary like below in my messageBody please?
NSLog(#"Dictionary: %#", [_sectionContents description]);//test dictionary here
Where _sectionContents is my dictionary.
You can attach you dictionary by the same code which you have written.
Here is the code for you.
NSString *emailTitle = #"Course Planner of iBcChem";
NSString *messageBody = #"Please check my course plan";
NSDictionary *dci = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:#"1",#"2", nil] forKeys:[NSArray arrayWithObjects:#"A",#"B", nil]];
NSArray *toRecipents = [NSArray arrayWithObject:#"xxxxx#gmail.com"];
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setSubject:emailTitle];
[mc setMessageBody:dci.description isHTML:NO];
[mc setToRecipients:toRecipents];
[self presentViewController:mc animated:YES completion:NULL];
In simulator mail delegate will take some time to load the Body part of mailcomposersheet. The body will look like.
{
A = 1;
B = 2;
}
Enjoy Coding !!
I have seen a nice feature in the iOS App Scanner Pro. This app allows to send scanned documents as email attachments via the original mail app from Apple but without leaving the Scanner Pro app. I ask me how did they do it? Is there a special API call?
implement MFMailComposeViewControllerDelegate like this:
#interface YourViewController<MFMailComposeViewControllerDelegate >
Then where you want to instantiate this email viewcontroller just do the following:
if([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init];
[mailController setMailComposeDelegate:self];
[mailController setSubject:#"Mail Subject!"];
[mailController setMessageBody:#"Here is your message body" isHTML:NO];
[mailController setToRecipients:[NSArray arrayWithObject:#"yourrecipent#domain.com"]];
NSData *imageData = UIImageJPEGRepresentation(imageToUpload, 1.0f);
if(imageData.length)
{
[mailController addAttachmentData:imageData mimeType:#"image/jpeg" fileName:#"Your_Photo.jpg"];
[self presentModalViewController:mailController animated:YES];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Invalid Image" message:#"The image couldn't be converted." delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Okay", nil];
[alert show];
}
}
Last implement mailComposerViewController delegate method
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
[self dismissViewControllerAnimated:YES completion:nil];
// or you can check for the status first and implement different task if you wish
}
You can use UIActivityViewController, for example:
UIImage *image = [UIImage imageNamed:#"image_file_name"];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[image] applicationActivities:nil];
[self presentViewController:activityViewController animated:YES completion:nil];
it gives user even more options, than just send email.
Yes, the so called UIActivityViewController. You use it like this:
NSArray *itemsToShare = #[[NSString stringWithFormat:#"This is a string that is sent via mail as well."], NSURLtoTheFileToSend];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:itemsToShare applicationActivities:nil];
activityVC.excludedActivityTypes = #[UIActivityTypeAssignToContact]; // Here you can say what you dont want to give the opportunity to share.
activityVC.completionHandler = ^(NSString *activityType, BOOL completed) {
if (completed) {
UIAlertView *alert = [[UIAlertView alloc] init];
alert.title = #"Export successfull";
[alert show];
[alert performSelector:#selector(dismissWithClickedButtonIndex:animated:) withObject:nil afterDelay:1];
}
};
[self presentViewController:activityVC animated:YES completion:^{}];
My problem is i have a view with a label, wherein, the data on label is coming from web service and on tapping it mail box should appear.In a nutshell it is a mailLabel.
Similarly on the same view i have a custom cell which too has another mail label and the same thing should happen,but the mail address will be different and dynamic.
Q1) do i need to include only one method of mail for this to handle.
Q2) If yes then how and if no then what is the procedure.
i have used a second method for this and called this in cellForRow like
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITapGestureRecognizer *mgmtMail1LblGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(mail1LblTappedForCC:)];
[cell.managementMemberEmail addGestureRecognizer:mgmtMail1LblGesture];
and method.
- (void)mail1LblTappedForCC:(id)sender
{
if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
[mailer setSubject:#""];
NSArray *toRecipients = [NSArray arrayWithObjects:objCCforManagement.managementMemberEmail.text, nil];
[mailer setToRecipients:toRecipients];
NSString *emailBody = #"";
[mailer setMessageBody:emailBody isHTML:NO];
mailer.navigationBar.barStyle = UIBarStyleBlackOpaque;
[self presentViewController:mailer animated:YES completion:nil];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Failure"
message:#"Your device doesn't support the composer sheet"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
where
objCCforManagement is the object of custom class.
How your recognizer will get the to recipient array from
NSArray *toRecipients = [NSArray arrayWithObjects:objCCforManagement.managementMemberEmail.text, nil];
Use recognizer.view to get your label text or get value from objCCforManagement on the basis of label.text. Or particular tag set to label like label.tag=cellRowIndex etc in cellForRow method
Code ----
cell.managementMemberEmail.tag=indexPath.row;
Your mail1LblTappedForCC method is unable to find which row value or objCCforManagement is to be inserted as recipient. thats why it is showing blank.
Get your email on the basis of row tag by setting it to label.
- (void)mail1LblTappedForCC:(UITapGestureRecognizer*)recognizer
{
if ([MFMailComposeViewController canSendMail])
{
UILabel *labelOnWhichItisClicked=(UILabel*)recognizer.view;
CustomCellForExhibitorDetailVC *cell=(CustomCellForExhibitorDetailVC*)[managementTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:labelOnWhichItisClicked.tag inSection:0]];
NSLog(#"mail to is == %#",cell.managementMemberEmail.text);
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
[mailer setSubject:#""];
NSArray *toRecipients =[NSArray arrayWithObjects:cell.managementMemberEmail.text,nil];
[mailer setToRecipients:toRecipients];
NSString *emailBody = #"";
[mailer setMessageBody:emailBody isHTML:NO];
mailer.navigationBar.barStyle = UIBarStyleBlackOpaque;
[self presentViewController:mailer animated:YES completion:nil];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Failure"
message:#"Your device doesn't support the composer sheet"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
i am a newbie to iOS and am trying to add inapt email in my app. I have a screen in which pushing the email icon should open the inapp email. I have the code for the inapp email. However, the button is already an outlet on the controller. So, I don't know how to link the same button to a different class/file which has the code for the inapp email. I was thinking of setting up a delegate but don't know how to initialize the delegate in the mail class. Have been struggling for a few days...please help!
Sumit
Try MFMailComposeViewController.... here is some sample code:
Make sure you import the MEssageUI framework and import the MFMailComposeViewController/MessageUI in .h and also conform to its delegate
MFMailComposeViewController *mailView = [[MFMailComposeViewController alloc] init];
[mailView setMailComposeDelegate:self];
if ([MFMailComposeViewController canSendMail]) {
[mailView setSubject:#"Interesting Apple News Article!"];
NSString *mailString = [[NSString alloc] initWithFormat:#"Test!"];
[mailView setMessageBody:mailString isHTML:NO];
[mailView setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentModalViewController:mailView animated:YES];
[mailString release];
[mailView release];
} else
[mailView release];
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
if (error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Mailing Error" message:[error localizedDescription] delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
[alert release];
[self dismissModalViewControllerAnimated:YES];
} else {
[self dismissModalViewControllerAnimated:YES];
}
}