How to share things with a UIActivityViewController

Screen Shot 2015-10-17 at 15.13.52

Since iOS 6 it has been really easy to share many complex objects, thanks to the UIActivityViewController. All we have to do is wrap our object (or objects) in an array, give that to the activity view controller, and present it.

To share a UIImage on iPhone for example, we’ll do something like this:

- (IBAction)shareImageDefault:(id)sender {
    
    // grab an item we want to share
    UIImage *image = [UIImage imageNamed:@"three"];
    NSArray *items = @[image];
    
    // build an activity view controller
    UIActivityViewController *controller = [[UIActivityViewController alloc]initWithActivityItems:items applicationActivities:nil];
    
    // and present it
    [self presentViewController:controller animated:YES completion:^{
        // executes after the user selects something
    }];
}

We need the array because we could be sharing more than just one item. The result looks something like the screenshot above: two rows of shareable icons appear, the top one representing “share” options, and the bottom one representing “action” options. Each of these is a UIActivity object. If ever you need to create your own activities, you can add them to either top or bottom category (but we won’t cover how to do that here).

On iPad, this type of presentation will crash – and Apple would like us to use a Popover presentation instead. It gets a bit long to show this here every time, so I’ve refactored this call into its own method and will be referring to it going forward:

- (void)presentActivityController:(UIActivityViewController *)controller {
    
    // for iPad: make the presentation a Popover
    controller.modalPresentationStyle = UIModalPresentationPopover;
    [self presentViewController:controller animated:YES completion:nil];
    
    UIPopoverPresentationController *popController = [controller popoverPresentationController];
    popController.permittedArrowDirections = UIPopoverArrowDirectionAny;
    popController.barButtonItem = self.navigationItem.leftBarButtonItem;

 

Excluding certain activities

If called as mentioned above, iOS will compile a list of all possible activities our item can be shared as. UIImages for example could be saved to our Photo Library, Air-dropped, Facebooked or emailed, among other things.

To trim this list down to something more relevant, we can exclude certain activities. Imagine we don’t want the option to share on Facebook and Twitter, we can do it like this:

- (IBAction)shareImageExcludeSocial:(id)sender {
    
    // grab an item we want to share
    UIImage *image = [UIImage imageNamed:@"three"];
    NSArray *items = @[image];
    
    // build an activity view controller
    UIActivityViewController *controller = [[UIActivityViewController alloc]initWithActivityItems:items applicationActivities:nil];
    
    // exclude several items
    NSArray *excluded = @[UIActivityTypePostToFacebook, UIActivityTypePostToTwitter];
    controller.excludedActivityTypes = excluded;
    
    // and present it
    [self presentActivityController:controller];
}

The UIActivity class has a type property, each of which is an NSString, and Xcode knows them all. You can literally exclude anything and everything from this list (check the class reference below for all types you can include here).

Reacting to user selections

The UIActivityViewController calls a completion handler block we can access. In it we’re given the activity  the user selected (as reverse-domain string), a BOOL ti indicate if the user used a sharing option (YES) or if the cancel button was pressed (NO), and error object and possible returned objects.

Sadly the Apple documentation isn’t very obvious how to do this at the time of writing, and it took some tinkering to find this out:

// access the completion handler
controller.completionWithItemsHandler = ^(NSString *activityType,
                                          BOOL completed,
                                          NSArray *returnedItems,
                                          NSError *error){
    // react to the completion
    if (completed) {
        
        // user shared an item
        NSLog(@"We used activity type%@", activityType);
        
    } else {
        
        // user cancelled
        NSLog(@"We didn't want to share anything after all.");
    }
    
    if (error) {
        NSLog(@"An Error occured: %@, %@", error.localizedDescription, error.localizedFailureReason);
    }
};

In essence, we need to create our own block through which the above objects are available, and add it to our controller. It’s slightly different from using a block that’s part of a method signature or implementing delegate methods – but this approach works just the same.

The activityType will be something like typecom.apple.UIKit.activity.SaveToCameraRoll, through which you can test which activity was selected by the user. If the completed BOOL returns NO, the cancel button was pressed.

Demo Project

I’ve got a working demo project on GitHub. Feel free to take a look at it:

Further Reading





3 thoughts on “How to share things with a UIActivityViewController

  1. Well presented. One thing I’m a problem with is sharing UIImages through email on the iPad. Everything else works fine. But specifically sharing over email on an iPad makes it just return with “completed” set to NO as soon as user taps the Mail icon. Very strange that I can’t seem to find any mention off it by anyone else.

Leave a Reply