'Application tried to present a nil modal view controller on target' error/crash when trying to open mail composer - iphone

I have a simple app, which opens a modal view to send email. Am using Xcode 4.2 and iOS 5, and am testing with iOS Simulator. The app crashes with
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'Application tried to present a nil modal view controller on target
.'
when executing the line:
[self presentModalViewController:mailComposer animated:YES];
though I have initialized the object 'mailComposer'.
Class com_FirstViewController.m :
#import "com_FirstViewController.h"
...
#implementation com_FirstViewController
....
....
-(void)showEmailComposer {
Class mailClass = (NSClassFromString(#"MFMailComposeViewController"));
if (mailClass != nil)
{
if ([mailClass canSendMail]) {
NSLog(#"showEmailComposer: Calling displayComposerSheet");
[self displayComposerSheet];
} else {
NSLog(#"showEmailComposer: Calling launchMailAppOnDevice");
[self launchMailAppOnDevice];
}
}
else {
NSLog(#"showEmailComposer: Calling launchMailAppOnDevice");
[self launchMailAppOnDevice];
}
}
#pragma mark -
#pragma mark Compose Mail
-(void) displayComposerSheet {
mailComposer = [[MFMessageComposeViewController alloc] init];
mailComposer.messageComposeDelegate = self;
// Set the mail title
[mailComposer setTitle:#"Mail Title"];
// Set the recipients
NSArray *toRecipients = [NSArray arrayWithObject:#"user#company.com"];
[mailComposer setRecipients:toRecipients];
// EMail Body
NSString *mailBody = #"This is the mail body";
[mailComposer setBody:mailBody];
NSLog(#"present the modal view ctlr");
[self presentModalViewController:mailComposer animated:YES];
}
...
...
Any pointers please?

I have also encountered a similar problem. I allocated an instance of MFMailComposeViewController and tried to present it modally. I also got an exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present a nil modal view controller on target
This was because the Mail option was disabled in the settings of the iPhone. It can be the case also when no mail account is set up in the device. So the MFMailCompseViewController instance will be nil and presenting it modally will cause the crash.
I used the canSendMail method of the MFMailComposeViewController to get around this issue.
Class mailClass = (NSClassFromString(#"MFMailComposeViewController"));
if (mailClass != nil) {
MFMailComposeViewController * mailView = [[MFMailComposeViewController alloc] init];
mailView.mailComposeDelegate = self;
//Set the subject
[mailView setSubject:emailSubject];
//Set the mail body
[mailView setMessageBody:emailBody isHTML:YES];
//Display Email Composer
if([mailClass canSendMail]) {
[self.navControl presentModalViewController:mailView animated:YES];
}
}

mailComposer = [[MFMessageComposeViewController alloc] init];
is the source of the problem, in my opinion. There's no way the simulator can send SMS messages, so the initializer method may return NULL. Anyways, sou seem to be wanting to send an email, so I'd say you need to use
mailComposer = [[MFMailComposeViewController alloc] init];

Related

How to remove association from a view and a view controller?

When I try to show the facebook share after having sent an email (using MFMailComposeViewController) I get this error:
A view can only be associated with at most one view controller at a
time! View [EAGLView] is associated with [EmailViewController]. Clear
this association before associating this view with [FacebookView].'
[EmailViewController removeFromParentViewController]; Does nothing
EmailViewController.view = nil; Causes a white screen, even though the email form is long gone.
How to make it forget that I ever sent and email and make the view hierarchy go back to how it was before? The facebook share works if I haven't sent an email.
-(IBAction)ShowEmailForm:(char*)pSubject :(char*)pBody :(char*)pTo
{
Class mailClass = (NSClassFromString(#"MFMailComposeViewController"));
if (mailClass != nil)
{
if ([mailClass canSendMail])
{
self.view = eaglView;
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:[NSString stringWithFormat:#"%s", pSubject]];
// Set up recipients
if( pTo != nil )
{
NSArray *toRecipients = [NSArray arrayWithObject:[NSString stringWithFormat:#"%s", pTo]];
[picker setToRecipients:toRecipients];
}
// Fill out the email body text
[picker setMessageBody:[NSString stringWithFormat:#"%s",pBody] isHTML:YES];
[self presentViewController:picker animated:YES completion:^(){}];
[picker release];
}
}
}
// Dismisses the email composition interface when users tap Cancel or Send. Proceeds to update the message field with the result of the operation.
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
[self dismissViewControllerAnimated:YES completion:^(){ printf("Email form done dismissing.\n"); }];
printf("Email form dismissed.\n");
[self removeFromParentViewController];
//Email was sent.
if (result == MFMailComposeResultSent)
{
printf("Email Sent!\n");
NSString *pEmail = [self findEmailAddresses:controller.view : 0];
}
}
Fixed it.
Change
self.view = eaglView;
to
[eaglView addSubview:self.view];

Cancel button on MFMessageComposeViewController does not show up

In my iPhone application, I have just implemented in-app SMS functionality. SMS functionality is working fine. But after opening MFMessageComposeViewController, if user wants to cancel sending sms, they have no option. The only option left is to send an sms, then only return on previous view. There should be a cancel button on navigation bar just as it is in the email composer. Below is the line of code that I wrote to have in-app sms functionality:
-(void) smsComposer{
MFMessageComposeViewController *_smsCompose = [[MFMessageComposeViewController alloc] init];
if ([MFMessageComposeViewController canSendText]) {
_smsCompose.body = #"SMS BODY";
_smsCompose.messageComposeDelegate = self;
[self presentModalViewController:_smsCompose animated:YES];
}
}
Is there anything that I am missing?
Thanks in advance,
PC
Try This....
in .h file
#import <MessageUI/MFMessageComposeViewController.h>
and
#interface TestViewController : UIViewController <MFMessageComposeViewControllerDelegate>
And then Button Click method
-(void)buttonPressed:(UIButton *)button
{
[self sendSMS:#"Body of SMS..." recipientList:[NSArray arrayWithObjects:#"+1-111-222-3333", #"111-333-4444", nil]];
}
MFMessageComposeViewController to create the SMS content and another method for handling the user interaction with the SMS dialog.
-(void)sendSMS:(NSString *)bodyOfMessage recipientList:(NSArray *)recipients
{
MFMessageComposeViewController *controller = [[[MFMessageComposeViewController alloc] init] autorelease];
if([MFMessageComposeViewController canSendText])
{
controller.body = bodyOfMessage;
controller.recipients = recipients;
controller.messageComposeDelegate = self;
[self presentModalViewController:controller animated:YES];
}
}
And
-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
[self dismissModalViewControllerAnimated:YES];
if (result == MessageComposeResultCancelled)
NSLog(#"Message cancelled")
else if (result == MessageComposeResultSent)
NSLog(#"Message sent")
else
NSLog(#"Message failed")
}
And remember: You cannot send SMS messages from within the simulator. Test on Device.

App crashes when dismiss mail composer and show a HUD

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];

SMS option not working properly

I am using xcode 4.2 and i am developing an iphone APP , part of this app is sending SMSs
here is the code that I typed:
-(IBAction)SMSbutton{
MFMessageComposeViewController *sms = [[MFMessageComposeViewController alloc] init];
if ([MFMessageComposeViewController canSendText]) {
sms.body= [NSString stringWithFormat: #"text"];
sms.messageComposeDelegate = self;
[self presentModalViewController:sms animated:YES];
}
I can open the SMS application and send SMS but the problem is whether I click on the "cancel" or the "send" button the message application does not go away . how to do that ?
thanks
Set your calling class to be a delegate of the message composer, then catch the cancel and send events to dismiss the modal view controller as needed.
Set your header file to adhere to the MFMessageComposeViewControllerDelegate, and when you initialize the composer, set the message delegate to self:
MFMessageComposeViewController *george = [MFMessageComposeViewController alloc] init];
george.messageComposeDelegate = self;
Then implement the delegate method...easiest way is:
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
[self dismissModalViewControllerAnimated:YES];
}
...but you can catch and deal with errors and events as necessary.
Implement messageComposeViewController:didFinishWithResult: in your delegate"
-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result {
if(result == MessageComposeResultCancelled) {
//Message cancelled
} else if(result == MessageComposeResultSent) {
//Message sent
}
[self dismissModalViewControllerAnimated:YES];
}
Have you tried dismissViewControllerAnimated:completion: or dismissModalViewControllerAnimated:?
[self dismissModalViewControllerAnimated: YES];

Can't set recipients of MFMessageComposeViewController?

I have a method like this:
void sendSMS{
if ([MFMessageComposeViewController canSendText]) {
MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
picker.recipients = [NSArray arrayWithObject:#"0933660805"];
[picker setBody:#"Message body"];
picker.messageComposeDelegate = self;
[self.navigationController presentModalViewController:picker animated:YES];
//[picker release];
return;
}
}
Message composer open but recipients and message body are empty (image below). Anybody know how can i fix it :(
Go for this ones and then check may be it will resolve your issue
void sendSMS
{
if ([MFMessageComposeViewController canSendText]) {
MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
picker.messageComposeDelegate = self;
NSString *bodyString = nil;
NSMutableArray *toRecipients = [[NSMutableArray alloc]init];
[toRecipients addObject:#"0933660805"];
[picker setRecipients:(NSArray *)toRecipients];
[toRecipients release];
bodyString = [NSString stringWithFormat: #"Message body"];
[picker setBody:bodyString];
[self presentModalViewController:picker animated:YES];
[picker release];
}
Also take a look at this tutorial http://blog.mugunthkumar.com/coding/iphone-tutorial-how-to-send-in-app-sms/
Good Luck!
OK I answered my own question. Now I want no one else to have to go thru this. I was calling this method from just an NSObject. It was a delegate to MFMessageComposeViewControllerDelegate but that made no difference. I had to move this method to my MainViewController, then it worked.
iOS 10.0 is here and this is still a problem for me. So, I have fashioned a workaround.
According to previous comments that initializing the MFMessageComposeViewController in the viewDidLoad() won't solve the problem (which I can attest to), unless the view controller gets presented, it won't be cached. So, the hack here is to make a window, set its root view controller, present a dummy MFMessageComposeViewController instance and immediately dismiss it, somewhere before your actual need (like in viewDidLoad())
Here is a sample code I'm using (Swift 3.0 - Let me know if you were interested in Obj-C counterpart):
let window = UIWindow()
let vc = UIViewController()
window.rootViewController = vc
let messageCompose = MFMessageComposeViewController()
vc.present(messageCompose, animated: false) { [weak messageCompose] in
messageCompose?.dismiss(animated: false, completion: nil)
}
The thing here is that if you present it in the currently active window's view controller chain, it will mess up your UI by showing and hiding the keyboard abruptly (no matter how you try to hide the controller's view and what not), due to the message body selection on present. But adding it to a whole new window which is not in view cycle, it will be correctly initialized and there will be no trace of such transaction on view. Plus, you won't boggle the memory too much this way (because the scope of the controller should be minimal now) and you can initialize your actual MFMessageComposeViewController any time you want and get it much faster now. If your application heavily relies on MFMessageComposeViewController (which I doubt) you can move this to your AppDelegate to be ready anywhere around your app's life cycle.
Cheers,
M.
Try this
- (void)sendSMS
{
if ([MFMessageComposeViewController canSendText]) {
MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
picker.messageComposeDelegate = self;
NSString *bodyString = nil;
NSArray *toRecipients = [NSArray arrayWithObject:#"NUMBER HERE"];
[picker setRecipients:toRecipients];
[self presentModalViewController:picker animated:YES];
[picker release];
}
In my case (on iPhone 3g s) the problem was when I called [self.navigationController pushViewController... ], when i tried call [self presentModalViewController ...] it worked, I dont know why, but it is. Try it.
set the MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
before if ([MFMessageComposeViewController canSendText]) {...}
Try this.
Try this.
- (void)forwardPromo
{
MFMessageComposeViewController *composeViewController = [[MFMessageComposeViewController alloc] init];
composeViewController.body = #"Message body";
composeViewController.recipients = [NSArray arrayWithObject:#"0933660805"];
composeViewController.messageComposeDelegate = self;
[self presentViewController:composeViewController animated:YES completion:nil];
}
You should have a "nil" at the end of the array:
composeViewController.recipients = [NSArray arrayWithObject:#"0933660805", nil];