Last week at work I found myself into interesting problem. We had to show the share action sheet. Not just Facebook, Twitter or Google plus, but the one iOS natively offers like this,

activity_view_controller

At the first look it looked quite daunting. So as usual I inflated my estimate to 4 hours. Though I was not quite sure if that would be enough in spite of escalated time.

  • Creating custom UIActivity objects:
    In the following paragraph we will create two custom application activities as follows

  • Custom application activity object to show Google search


class CustomActivityOne: UIActivity {
    override func activityType() -> String? {
        return "jayesh.activityType.Search"
    }
    
    override func activityTitle() -> String? {
        return "Search Engine"
    }
    
    override func activityImage() -> UIImage? {
        return UIImage(named: "google")
    }
    
    override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
        return true
    }
    
    override func prepareWithActivityItems(activityItems: [AnyObject]) {
        
    }
    
    override func performActivity() {
        UIApplication.sharedApplication().openURL(NSURL(string: "https://www.google.com")!)
    }
}
  • Custom application activity object to show custom view controller

class CustomActivityTwo: UIActivity {
    
    var navController: UINavigationController = UINavigationController()
    
    override func activityType() -> String? {
        return "jayesh.activityType.Obama"
    }
    
    override func activityTitle() -> String? {
        return "President"
    }
    
    override func activityImage() -> UIImage? {
        return UIImage(named: "obama_icon")
    }
    
    override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
        return true
    }
    
    override func prepareWithActivityItems(activityItems: [AnyObject]) {
        
    }
    
    override func activityViewController() -> UIViewController? {
        let presidentViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("president")
        navController = UINavigationController(rootViewController: presidentViewController)
        presidentViewController.title = "Barack Obama"
        presidentViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Cancel, target: self, action: #selector(dismissViewController))
        return navController
    }
    
    func dismissViewController() {
        navController.dismissViewControllerAnimated(true, completion: nil)
        activityDidFinish(true)
    }
}

So we have created two custom application activity objects. We will then add these custom objects to our UIActivityViewController during initialization.

We will first create a button. When user presses this button, we will present an activity view controller

activity_view_controller_button

activity_view_controller

As you can see in the above screenshot, two custom application activities I have added are visible in the activities list. Viz. Google Search and President


@IBAction func openActivityControllerButtonPressed(sender: UIButton) {
        
        let activityItems = ["Please check out my website", NSURL(string: "https://www.jayeshkawli.ghost.io")!, "Awesome Donuts", UIImage(named: "donuts.jpg")!]
        
        let searchActivity = CustomActivityOne()
        let presidentActivity = CustomActivityTwo()
        let customActivityTypes = [searchActivity, presidentActivity]
        
        let activityIndicatorView = UIActivityViewController(activityItems: activityItems, applicationActivities: customActivityTypes)
        
        activityIndicatorView.popoverPresentationController?.sourceView = sender
        activityIndicatorView.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
            if let activityName = activityType?.componentsSeparatedByString(".").last {
                let completedString = completed ? "Completed" : "Cancelled"
                let operationFinishMessage = "Activity '\(activityName)' \(completedString)"
                self.showMessage(operationFinishMessage)
            }
        }
        self.presentViewController(activityIndicatorView, animated: true, completion: nil)
    }

Also, when user performs any action from activity items, based on the action taken, we will also show an alert dialogue whether action was completed or cancelled.


func showMessage(alertDetail: String) {
        let alertController = UIAlertController(title: "Activity View Controller Demo", message: alertDetail, preferredStyle: .Alert)
        let alertAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
        alertController.addAction(alertAction)
        self.presentViewController(alertController, animated: true, completion: nil)
    }
  • Please note that in addition to application activities, I have also added another set of activityItems as follows :
  1. Item to share an URL. User can add it to reading list of open with Safari

  2. Item to share an image. User can store it in the photos or share it though a message


 let activityItems = ["Please check out my website", NSURL(string: "https://www.jayeshkawli.ghost.io")!, "Awesome Donuts", UIImage(named: "donuts.jpg")!]

And then we initialize out UIActivityViewController with these custom activities and present it on the viewport.


let activityIndicatorView = UIActivityViewController(activityItems: activityItems, applicationActivities: customActivityTypes)
self.presentViewController(activityIndicatorView, animated: true, completion: nil)

You can also get a callback when user selects activity items from list, action is successfully performed or cancelled as follows :


activityIndicatorView.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
            if let activityName = activityType?.componentsSeparatedByString(".").last {
                let completedString = completed ? "Completed" : "Cancelled"
                let operationFinishMessage = "Activity '\(activityName)' \(completedString)"
                self.showMessage(operationFinishMessage)
            }
        }

I have hosted the full code for this project on GitHub too. Feel free to give any suggestions or comments. Any pull requests or critics are most welcome.