1

First post, so I'll try to be direct and as detailed as possible... I'm using the UIActivityViewController and subclassing UIActivityItemProvider to handle sharing text in different ways depending on sharing method chosen (this thread helped a lot: How to know which icon is clicked in UIActivityViewController before activityController setCompletionHandleris called?).

I think I've figured out the mechanism of getting my source data passed in and then processing based on the itemForActivityType chosen, but for email sharing, I want to pre-populate the header (subject, body, etc) as well as attach a file. I know how to do this with the stand-alone MFMailComposeViewController, but I don't quite see how to do this using the ActivityViewController. Over here: UIActivityViewController - Email and Twitter sharing, there was mention of using a value/key pair on the initializer like:

UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities];
[activityViewController setValue:@"My Subject Text" forKey:@"subject"];

but what are the other standard keys for setting recipients and message body?

Attaching a document is the other big question I have. I know how to create a document and save it on the file system and attaching it - again using the MFMailComposeViewController - but I want to get the same behavior using the UIActivityViewController.

(sorry for the verbosity...)

Community
  • 1
  • 1

2 Answers2

0

To set the subject you should use - (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType

For your data (text and image), you can pass them via activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType{ this will return a UTI for your data so the service can better handle the data. The key you use isn't critical as the UTI will help to identify the items. Apple Doc:

Providing the UTI allows services to handle specific data types in appropriate ways, such as an email service formatting an image to display in-line. More info here

rmp
  • 3,503
  • 1
  • 17
  • 26
  • Okay, so can that method also be used to add an attachment if the UIActivityType is a mail message? – melbournejohn Jul 02 '15 at 10:47
  • Yes, iOS has a UTI for images so it will recognize an image being passed in as data and attached it. Well, actually it will add it inline in the case of email. – rmp Jul 02 '15 at 14:46
  • I guess I meant the generic case of adding an attachment of any type. Specifically I'm creating a .TXT doc using data from various fields on view controller and I want to attach the text file to a mail message. – melbournejohn Jul 04 '15 at 11:05
  • A .txt file should work fine as it is one of the standard UTI types defined in iOS. The mail app should recognize it and attach it. – rmp Jul 04 '15 at 16:11
0

I don't think you can directly access the mail controller without creating your own UIActivity subclass (YourActivity).

Set up your MFMailComposeViewController in "YourActivity" and it will operate as it did in your main code. Here is how I did it:

In YourActivity.h:

Make yourself the mail controller delegate and set up method wide variables for the mail view controller and the selected view controller:

@interface YourActivity : UIActivity <MFMailComposeViewControllerDelegate>
{
    MFMailComposeViewController *mailController;
    UIViewController *activityViewController;
}

In YourActivity.m:

(Optional) I recommend you check for the availability of mail services early. (This will prevent the user from being offered an option that can't be completed):

- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems
{
    // If mail is unavailable, can't perform activity
    if (![MFMailComposeViewController canSendMail]) {
        return NO;
    }

    for (id item in activityItems) {
        // whatever other checks you want to do
        return YES;
    }
    return NO;
}

In YourActivity -prepareWithActivityItems: method set up your MFMailComposeViewController:

- (void)prepareWithActivityItems:(NSArray *)activityItems
{
    // See if we can send mail (shouldn't happen if we checked already in -canPerformActivityWithItems)
    if (![MFMailComposeViewController canSendMail]) {

        UIAlertController *mailAlertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Mail Unavailable", @"mail unavailable")
                                                                                     message:nil
                                                                              preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"cancel")
                                                         style:UIAlertActionStyleCancel
                                                       handler:^(UIAlertAction * _Nonnull action) {
                                                           [self activityDidFinish:NO];
                                                       }];

        [mailAlertController addAction:cancel];

        // Set the alert as the view to return
        activityViewController = mailAlertController;
    }

    // Create a mail view controller
    mailController = [[MFMailComposeViewController alloc] init];

    // Set Delegate
    [mailController setMailComposeDelegate:self];

    // Set mail controller as the view to return
    activityViewController = mailController;

    // Paste the rest of your MFMailComposeViewController code here
}

In YourActivity -activityViewController method return your selected view controller:

- (UIViewController *)activityViewController
{
    return activityViewController;
}

Remember to implement the mail finish handler, at a minimum to dismiss the composer view:

- (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
    // Send any messages, if desired, to the controller before dismissing
    NSString *message = nil;
    NSString *errorMessage = nil;

    if (result == MFMailComposeResultFailed) {
        message = NSLocalizedString(@"Unable to send email", @"Unable to send email");
    }

    if (error) {
        errorMessage = [message stringByAppendingString:[NSString stringWithFormat:NSLocalizedString(@"Error:\n%@", @"error:\n%@"), [error localizedDescription]]];
    }

    // Send mail status alert message, if needed
    if (message) {
        UIAlertController *mailAlert = [UIAlertController alertControllerWithTitle:message
                                                                           message:errorMessage
                                                                    preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK")
                                                         style:UIAlertActionStyleCancel
                                                       handler:^(UIAlertAction * _Nonnull action) {
                                                           // Dismiss the mail controller
                                                           [controller dismissViewControllerAnimated:true completion:^{}];

                                                           [controller release];
                                                           mailController = nil;
                                                       }];

        [mailAlert addAction:cancel];

        [controller presentViewController:mailAlert animated:YES completion:^{
            //
        }];

    }

    else {
        // Dismiss the mail controller
        [controller dismissViewControllerAnimated:true completion:^{}];

        [controller release];
        mailController = nil;
    }
}
Peter
  • 201
  • 2
  • 4