Crash when using MFMailComposeViewController with UIActionSheet
I'm using the MFMailComposeViewController
to send mail from within an application. I have added the code from the Apple example to a UITableViewController
, and all works as expected when I trigger the modalViewController from a UIToolBarButton
. The problem comes when I put a UIActionSheet
between the UIToolBarButton
and the MFMailComposeViewController
code.
I want to present the user with the option to send by email or post to Facebook. When I call the MFMailComposeViewController
methods after the dismissal of the UIActionSheet
, my app crashes when the method tries to load the modalViewController. Code below, any ideas?
// UIToolBarButton generates the email string and displays the UIActionSheet with email options
- (void)onEmailButtonTouch
{
int mySection;
int myRow;
emailString = [NSString stringWithFormat:@"<b><p>Ten Essentials Check List</b><br />%@</p>", [myList valueForKey:@"listName"]];
for (mySection = 0; mySection < [[fetchedResultsController sections] count]; mySection ++)
{
NSString *sectionName = [NSString stringWithFormat:@"<p><b>%@ Group</b></p><ul>", [[[fetchedResultsController sections] objectAtIndex:mySection] name]];
emailString = [emailString stringByAppendingString:sectionName];
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:mySection];
for (myRow = 0; myRow < [sectionInfo numberOfObjects]; myRow ++)
{
// Get the managedObject
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:myRow inSection:mySection];
NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
//Get the related Item object
Item *item = [managedObject valueForKey:@"item"];
NSString *itemName = [NSString stringWithFormat:@"<li>%@</li>", item.itemName];
emailString = [emailString stringByAppendingString:itemName];
}
emailString = [emailString stringByAppendingString:@"</ul>"];
}
NSLog(@"email string = :\n%@", emailString);
[self showEmailOptions];
}
// Display an UIActionSheet with email/facebook buttons
-(void)showEmailOptions
{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"eMail Options" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:NULL otherButtonTitles:@"Send List via Email", @"Post List to Facebook", NULL];
[actionSheet showFromToolbar:self.navigationController.toolbar];
[actionSheet release];
}
// Call the MFMailComposeViewController method开发者_开发问答s if the user selects the Email button of the actioSheet
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0)
{
NSLog(@"Opening email");
[self showPicker];
}
}
-(void)showPicker
{
// This sample can run on devices running iPhone OS 2.0 or later
// The MFMailComposeViewController class is only available in iPhone OS 3.0 or later.
// So, we must verify the existence of the above class and provide a workaround for devices running
// earlier versions of the iPhone OS.
// We display an email composition interface if MFMailComposeViewController exists and the device can send emails.
// We launch the Mail application on the device, otherwise.
Class mailClass = (NSClassFromString(@"MFMailComposeViewController"));
if (mailClass != nil)
{
// We must always check whether the current device is configured for sending emails
if ([mailClass canSendMail])
{
[self displayComposerSheet];
}
else
{
[self launchMailAppOnDevice];
}
}
else
{
[self launchMailAppOnDevice];
}
}
// Displays an email composition interface inside the application. Populates all the Mail fields.
-(void)displayComposerSheet
{
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:@"Here is your gear check list!"];
// Attach an image to the email
NSString *path = [[NSBundle mainBundle] pathForResource:@"Checkmark_icon" ofType:@"png"];
NSData *myData = [NSData dataWithContentsOfFile:path];
[picker addAttachmentData:myData mimeType:@"image/png" fileName:@"Checkmark_icon"];
// Fill out the email body text
NSString *emailBody = emailString;
[picker setMessageBody:emailBody isHTML:YES];
// CRASH HAPPENS ON THE LINE BELOW //
[self presentModalViewController:picker animated:YES];
[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
{
//message.hidden = NO;
// Notifies users about errors associated with the interface
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
// Launches the Mail application on the device.
-(void)launchMailAppOnDevice
{
NSString *recipients = @"mailto:first@example.com?cc=second@example.com,third@example.com&subject=Here is your gear check list!";
NSString *body = emailString;
NSString *email = [NSString stringWithFormat:@"%@%@", recipients, body];
email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];
}
I think your problem is that you need
[emailString retain];
it's an autorelease object, and I think it's getting freed after onEmailButtonTouch
returns, so that it's invalid when your alert notification fires.
Actualy, David (above comment) put me on the right track. The UIActionSheet was causing my emailString ivar to become invalid, so I moved the call to the UIActionSheet to BEFORE building the emailString. Now all is working!
精彩评论