didFinishWithResult is not getting invoked for MFMailComposeViewController - iphone

in buttonpress callback:
MFMailComposeViewController *mailViewController = [[MFMailComposeViewController alloc] init];
mailViewController.mailComposeDelegate = self;
[self presentModalViewController:mailViewController animated:YES];
Delegate Implementation:
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
NSLog (#" Inside MAIL COMPOSER CONTROLLER DELIGATE ");
// Remove the mail view
[self dismissModalViewControllerAnimated:YES];
}
When i press cancel button in the MailComposerView, delete is not getting invoked. what am i doing wrong ?

Set your viewController as a MFMailComposeViewControllerDelegate:
#interface CurrentViewController : UIViewController <MFMailComposeViewControllerDelegate>
Set your mailComposer delegate right after instantiation:
MFMailComposeViewController * mailComposer = [[MFMailComposeViewController alloc]init];
mailComposer.mailComposeDelegate = self;

Did you actually make your class a MFMailComposeViewControllerDelegate?
#interface MyViewController : UIViewController <MFMailComposeViewControllerDelegate>

Related

Ios MFMailComposeViewController not displaing

I trying to write rhodes native extension extension for opening iOS MailComposer.
Now code "works", but MFMailComposeViewController not displaying and xcode shows warning:
Attempt to present <MFMailComposeViewController: 0x12b60840> on <Iosmailto: 0xc1898d0> whose view is not in the window hierarchy!
I read that UIViewControllers must be in hierarchy, but i can't provide this from Rhodes ruby code or clear-C extension wrapper.
Now i'm trying to present my UIViewController with MFMailComposeController from [UIApplication sharedApplication].keyWindow.rootViewController but mail composer not show yet.
interface from iosmailto.h:
#interface Iosmailto : UIViewController<MFMailComposeViewControllerDelegate>
{
}
#property(nonatomic, assign) id delegate;
//-(IBAction)send_mail:(id)sender;
#end
implementation from iosmailto.m:
#implementation Iosmailto
- (void)viewDidLoad {
[super viewDidLoad];
[self send_mail];
}
- (void)send_mail {
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *composer = [[MFMailComposeViewController alloc] init];
composer.mailComposeDelegate = self;
[composer setSubject:[NSString stringWithUTF8String:subj]];
NSArray *recipients_array = [NSArray arrayWithObject:[NSString stringWithUTF8String:recipients]];
[composer setToRecipients:recipients_array];
NSString *composerBody = [NSString stringWithUTF8String:message];
[composer setMessageBody:composerBody isHTML:NO];
[composer setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:composer animated:YES];
[composer release];
} else {
NSLog(#"Device is unable to send email in its current state.");
}
}
/* some unnecessary for this question methods */
#end
And C function defined in iosmailto.m thats called from Rhodes:
// find root vc
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
// find topmost vc
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
iosmailer = [[Iosmailto alloc] init];
iosmailer.delegate = topController;
[topController presentViewController:iosmailer animated:YES completion:nil];
This app is not for AppStore. Hacks is welcome if needed.
Update init for Iosmailto:
- (id)init
{
self = [super initWithNibName:nil bundle:nil];
return self;
}
Update 2 The short version of this code (without UIViewController wrapper) was my starting point. That's don't work too. And this have the same warning and one more:
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *composer = [[MFMailComposeViewController alloc] init];
composer.mailComposeDelegate = (id<MFMailComposeViewControllerDelegate>)topController ;
[composer setSubject:[NSString stringWithUTF8String:subj]];
NSArray *recipients_array = [NSArray arrayWithObject:[NSString stringWithUTF8String:recipients]];
[composer setToRecipients:recipients_array];
NSString *composerBody = [NSString stringWithUTF8String:message];
[composer setMessageBody:composerBody isHTML:NO];
[composer setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[topController presentModalViewController:composer animated:YES];
[composer release];
} else {
}
Use this code to present view controller
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:composer
animated:YES
completion:nil];
Ok I think the problem is that your UIViewController doesn't have anything in its view property because you aren't using a .xib file to initialize it. See this question which programmatically creates a UIView and assigns it to the UIViewController's view property. I think that's the right way to go. See the implementation of their -(void)loadView method.
Your use-case is also a little strange, since it looks like your Iosmailto UIViewController doesn't have any content, and you're just using it as a wrapper around MFMailComposeViewController--you might consider reimplementing your C method to directly create a MFMailComposeViewController without the unnecessary layer of indirection of this unnecessary UIViewController that doesn't display anything itself.

Presenting a modal view from another class (MFMailComposeViewController)

I am trying to create a class who will be responsible to send emails using MFMailComposeViewController so i can use this methods from differences views controls in my app.
This class is called apoio.
In this class have the method bellow.
-(void) enviarGraficoPorEmail: (NSData*) _pdfGrafico {
if (![MFMailComposeViewController canSendMail]) {
// show message box for user that SMS cannot be sent
} else {
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:#"Dashboard"];
[picker addAttachmentData:_pdfGrafico mimeType:#"application/pdf" fileName:#"grafico.pdf"];
NSString *emailBody = #"Anexando gráfico";
[picker setMessageBody:emailBody isHTML:NO];
[self presentModalViewController:picker animated:YES];
}
}
I have another view controller who calls the apoio method when the user clicks on email button. It is this code bellow
-(IBAction) enviarGraficoPorEmail {
Apoio *apoio = [[Apoio alloc] init];
[apoio enviarGraficoPorEmail:[barChart dataForPDFRepresentationOfLayer]];
}
But i don't know why, the email view doesn't appear. The method is called correct because i debuged and so on.
If i copy the code from apoio method to enviarGraficoPorEmail method, everything works perfect.
But i don't want to do this, beucase ill send emails from others views controller.
What am i doing wrong ??
You could do it a couple of different ways.
Option 1: Pass the calling view controller as a parameter to the class method
-(IBAction) enviarGraficoPorEmail {
Apoio *apoio = [[Apoio alloc] init];
[apoio enviarGraficoPorEmail:[barChart dataForPDFRepresentationOfLayer] callingController:self];
}
-(void) enviarGraficoPorEmail: (NSData*) _pdfGrafico callingController:(UIViewController*)_callingController {
...
[_callingController presentModalViewController:picker animated:YES];
...
}
Option 2: Add a class variable for the calling view controller
-(IBAction) enviarGraficoPorEmail {
Apoio *apoio = [[Apoio alloc] init];
apoio.callingController = self;
[apoio enviarGraficoPorEmail:[barChart dataForPDFRepresentationOfLayer]];
}
-(void) enviarGraficoPorEmail: (NSData*) _pdfGrafico callingController:(UIViewController*)_callingController {
...
[callingController presentModalViewController:picker animated:YES];
...
}
Then you'd add callingController to your class as a retain property, initialize it to nil, and release it in dealloc.
Option #1 is probably the better approach for your needs.
Thanks a lot!. It is now working but i still have one problem.
I have the method on my generic class who is supposed to be responsible to hide the mailController.
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
switch (result)
{
case MFMailComposeResultCancelled:
break;
case MFMailComposeResultSaved:
break;
case MFMailComposeResultSent:
// FAILS
[self.parentViewController dismissModalViewControllerAnimated:YES];
break;
case MFMailComposeResultFailed:
break;
default:
break;
}
[self dismissModalViewControllerAnimated:YES];
}
In the method that creates the mailController have the property
picker.mailComposeDelegate = self;
I tried to change to
picker.mailComposeDelegate = _callingController.self;
I set the MFMailComposeViewControllerDelegate on my generic class already.
But it only works when i copy the method didFinishWithResult and put it on the origin controller, what is not my intention, because i want to put all this code on a generic class.
What am i doing wrong ??
Ok, this is an answer to your second question (which you posted as an answer to your first question).
Here's how I would set it all up:
In your calling view controller .h file:
#interface MyViewController : UIViewController <MyMailDelegate> {
Apoio *apoio;
}
In your calling view controller .m file:
-(IBAction) enviarGraficoPorEmail {
apoio = [[Apoio alloc] init];
apoio.callingController = self;
[apoio enviarGraficoPorEmail:[barChart dataForPDFRepresentationOfLayer]];
}
-(void) enviarCompleto {
//do whatever here after send email completes
[apoio release];
}
In your Apoio .h file
#protocol MyMailDelegate
#required
-(void) enviarCompleto;
#end
#interface OfferObject : NSObject {
UIViewController <MyMailDelegate> *callingController;
}
#property (nonatomic, retain) UIViewController <MyMailDelegate> *callingController;
In your Apoio .m file
-(void) enviarGraficoPorEmail: (NSData*) _pdfGrafico callingController:(UIViewController*)_callingController {
...
[callingController presentModalViewController:picker animated:YES];
...
}
-(void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
switch (result)
{
...
}
[callingController dismissModalViewControllerAnimated:YES];
[callingController enviarCompleto];
}
Then don't forget to do this on init:
callingController = nil;
And on dealloc:
[callingController release];
Also, don't forget your most important step: up vote both my answers :)
You need to call [self presentModalViewController:picker animated:YES];
from a UIViewController class then it will show you email composer, you can do this, whenever you call from some view controller you can pass its reference and you can change the above line as this
[callingController presentModalViewController:picker animated:YES];

How can i call a mail function in Root ViewController?

I want to send a mail with attachment . In my app i created a separate class for the mail function and in rootViewController I have button Email It . When i click it My mail Function has to be called but it doesn't.
MFMailViewController *controller = [[MFMailViewController alloc] initWithNibName:"MFMailViewController" bundle:nil];
[[self navigationController] pushViewController:viewController animated:YES];
[viewController release];
After that i got know that i cant use the mail function like this. can any one help me find this
Its not MFMailViewController, its MFMailComposeViewController. Please go through this Apple reference to get more details.
Here is a sample code
if([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init];
if(mailController!=nil) {
mailController.mailComposeDelegate = self;
[mailController setSubject:urSubject];
[mailController setMessageBody:urBody isHTML:NO];
[mailController addAttachmentData:urData mimeType:type fileName:urfileName];
}
[self presentModalViewController:mailController animated:YES];
[mailController release];
}
Create an instance method in MFMailViewController class with some parameter like dictionary or NSArray
when you click on Email it button you are creating object of MFMailViewController then called the instance method using the controller object and pass the parameter or info to.
then push it's view to navigation.
#import <Foundation/Foundation.h>
#interface MFMailViewController : NSObject
{}
-(void)sendEmail:(NSMutableDictionary*)withData;
#end
#import "MFMailViewController.h"
#implementation MFMailViewController
-(void)sendEmail:(NSMutableDictionary*)withData;
{
//definition goes here.................
}
#end
MFMailViewController *controller = [[MFMailViewController alloc] initWithNibName:"MFMailViewController" bundle:nil];
[controller sendEmail:withData];
[[self navigationController] pushViewController:viewController animated:YES];
[viewController release];
simple where you want to send mail just define this code in you method .....
but first define delegate in .h file of your viewController
delegate is MFMailComposeViewControllerDelegate
and then in button click method write this code......
MFMailComposeViewController* controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
[controller setToRecipients:#"Recipients Name"];
[controller setSubject:#"your subject"];
[controller setMessageBody:#"Your Text Here for Mail" isHTML:YES];
if (controller)
{
[self presentModalViewController:controller animated:YES];
[controller release];
}
and then you can just paste bellow mailComposerDelegate method in yourViewController.m file.....
- (void)mailComposeController:(MFMailComposeViewController*)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError*)error;
{
if (result == MFMailComposeResultSent) {
NSLog(#"Mail Sent");
}
[self dismissModalViewControllerAnimated:YES];
}

MFMailComposeViewController in a separate class

I seek to create a "Utility Email sender class" that I can use in several iPhone projects.
I created MailSender header and implementation for that purpose.
MailSender.h:
#interface MailSender : NSObject<MFMailComposeViewControllerDelegate>
- (id) initWithParent:(UIViewController*) mainController;
- (void) invokeMailSender:(NSString*) to:(NSString*) subject:(NSString*) failureTitle:(NSString*) failureMessage:(NSString*) failureCancel;
#end
MailSender.m:
#import "MailSender.h"
#implementation MailSender
MFMailComposeViewController* mailer;
UIViewController* mailParentController;
- (id) initWithParent:(UIViewController*) mainController
{
if( self = [super init])
{
mailParentController = mainController;
}
return self;
}
- (void) invokeMailSender:(NSString*) to:(NSString*) subject:(NSString*) failureTitle:(NSString*) failureMessage:(NSString*) failureCancel;
{
if([MFMailComposeViewController canSendMail])
{
mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
[mailer setSubject:subject];
NSArray *toRecipients = [NSArray arrayWithObjects:to, nil];
[mailer setToRecipients:toRecipients];
[mailParentController presentModalViewController:mailer animated:YES];
}
else
{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:failureTitle message:failureMessage
delegate:nil cancelButtonTitle:failureCancel otherButtonTitles: nil];
[alert show];
}
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
// Do nothing
[mailParentController dismissModalViewControllerAnimated:YES];
mailer = nil;
}
#end
I called the class from a View Controller (in a button touch down action) using the following instructions:
#implementation InfoViewController
MailSender *sender;
- (IBAction)openMail:(id)sender
{
sender = [[MailSender alloc] initWithParent:self];
[sender invokeMailSender:#"test#test.com" :#"123" :#"123" :#"123" :#"123"];
}
....
#end
When I run the code, I am able to show the email views correctly. However, this is then followed by a crash.
Note that I do not have a crash when using MFMailComposeViewController directly from my UIViewController (And assigning the View Controller as the delegate),
Any ideas?
Sorry I am still a new to Objective C :)
You need to retain your sender MailSender instance. It is being released after you call the invoke message.
You could do this by declaring a property named sender. E.g.
#property (strong, nonatomic) MailSender *sender;
...
#synthesize sender = _sender;
...
self.sender = [[MailSender alloc] initWithParent:self];
[self.sender invokeMailSender:#"noor#dimachk.com" :#"123" :#"123" :#"123" :#"123"];
By the way, your method declaration is a bit funny. You should name the arguments. E.g.
- (void)invokeMailSender:(NSString *)sender
to:(NSString *)to
subject:(NSString *)subject
failureTitle:(NSString *)failureTitle
failureMessage:(NSString *)failureMessage
failureCancelButtonTitle:(NSString *)failureCancelButtonTitle

iPhone: How to Close MFMailComposeViewController?

I'm having difficulties closing an email message that I have raised.
The email opens nicely, but once it is opened it will not close as the mailComposeController:mailer didFinishWithResult:result error:error handler never gets invoked.
As far as I can tell I have all the bits in place to be able to do this.
Anyone any ideas of what I can look at?
Here is how I raise the email:
-(IBAction)emailButtonPressed
{
NSString *text = #"My Email Text";
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.delegate = self;
[mailer setSubject:#"Note"];
[mailer setMessageBody:text isHTML:NO];
[self presentModalViewController:mailer animated:YES];
[mailer release];
}
and later in the class I have this code to handle the close (but it never gets called):
-(void)mailComposeController:(MFMailComposeViewController *)mailer didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
[self becomeFirstResponder];
[mailer dismissModalViewControllerAnimated:YES];
}
My header file is defined as:
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
#interface myViewController : UIViewController <UIActionSheetDelegate, UIAlertViewDelegate, MFMailComposeViewControllerDelegate, UINavigationControllerDelegate>
Thanks
Iphaaw
You are setting the delegate wrong, the delegate property in MFMailComposeViewController is called mailComposeDelegate, so it should be:
mailer.mailComposeDelegate = self;
Another possible error I can see is calling dismissModalViewControllerAnimated: on mailer - you should send this message to the view controller who presented the mail interface - self in this case:
[self dismissModalViewControllerAnimated:YES];
I wrote "possible error" because it might actually work if iOS routes the message through responder chain, anyway - the documentation says it should be send to presenter.