On iOS, UIActivityViewController provides a unified interface for users to share and perform actions on text, images, URLs and other items in the app.
You create a UIActivityViewController by passing the items you want to share and any custom activities you want to support (we’ll show you how to do that later). Then, you present that UIActivityViewController.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | let string = "Hello, world!" let url = URL(string: "https://nshipster.com")! let image = UIImage(named: "mustache.jpg") let pdf = Bundle.main.url(forResource: "Q4 Projections", withExtension: "pdf") let activityViewController = UIActivityViewController(activityItems: [string, url, image, pdf], applicationActivities: nil) present(activityViewController, animated: true) { … } |
When you run this code, the screen displays the following information:
By default, the UIActivityViewController shows all the available activities for the provided items, but you can exclude certain types of activity via the excludedActivityTypes
property.
1 2 | activityViewController.excludedActivityTypes = [.postToFacebook] |
The activity types are divided into “action” and “sharing” categories:
- UIActivityCategoryAction (UIActivityCategoryAction) activity items that perform an action on selected content, such as copying text to a sticker board or printing an image.
- Share (UIActivityCategoryShare) selected content sharing activity items, such as composing a message containing a URL or posting images to Twitter.
Each activity type supports certain types of items. For example, you can post text, URL, and / or image on Twitter, but you can’t assign a character string as a picture for a contact.
The following tables show the activity types available for each category and their supported categories:
UIActivityCategoryAction
UIActivityCategoryShare
UIActivityViewController allows users to choose how they share content. However, as a developer you can directly access this functionality. Below are the corresponding APIs for each type of activity provided by the system:
Activity Type | Corresponding API |
---|---|
addToReadingList | SSReadingList |
assignToContact | CNContact |
UIPrintInteractionController | |
saveToCameraRoll | UIImageWriteToSavedPhotosAlbum |
MFMailComposeViewController | |
message | MFMailComposeViewController |
addToReadingList | MFMessageComposeViewController |
postToFacebook, postToFlickr, postToTencentWeibo, postToTwitter, postToVimeo, postToWeibo | SLComposeViewController |
Create custom UIActivity
In addition to the activities provided by the system, you can create your own.
For example, let’s create a custom activity that takes an image and applies a mustache to it through a web application.
Define your custom activity type
First, define a new activity type constant in the extension for UIActivity.ActivityType, which is initialized by the reverse DNS identifier.
1 2 3 4 5 | extension UIActivity.ActivityType { static let mustachify = UIActivity.ActivityType("com.nshipster.mustachify") } |
Create a UIActivity subclass
Next, create a subclass of UIActivity and override default implementations of the activityCategory
and activityType
, activityTitle
and activityImage
properties.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class MustachifyActivity: UIActivity { override class var activityCategory: UIActivity.Category { return .action } override var activityType: UIActivity.ActivityType? { return .mustachify } override var activityTitle: String? { return NSLocalizedString("Mustachify", comment: "activity title") } override var activityImage: UIImage? { return UIImage(named: "mustachify-icon") } … } |
Determine which item is actionable
The activities are responsible for determining whether they can work on a certain array of items by overriding the canPerform(withActivityItems:)
.
Our custom activity might work if any item is an image we define with some fancy sample matches on a for-in loop:
1 2 3 4 5 6 7 8 | override func canPerform(withActivityItems activityItems: [Any]) -> Bool { for case is UIImage in activityItems { return true } return false } |
Prepare for Action
Once an activity has determined that it can work with the specified items, it uses prepare (withActivityItems 🙂 to be ready to execute the activity.
In the case of our custom run we take the first image:
1 2 3 4 5 6 7 8 9 | var sourceImageData: Data? override func prepare(withActivityItems activityItems: [Any]) { for case let image as UIImage in activityItems { self.sourceImageData = image.pngData() return } } |
Perform Activity
The performance () method is the most important part of your performance. Because processing can take some time, this is an asynchronous method. However, due to the lack of a complete handler, you signal that the job has been done by calling activityDidFinish
Our custom operation delegates the categorization process to a web application using task data sent from the shared URLSession. If all goes well, the mustachioedImage property is set and the activityDidFinish is called with true to indicate that the operation successfully completed. If there is an error in the request or we are unable to create an image from the data provided, we call activityDidFinish with false to indicate an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | var mustachioedImage: UIImage? override func perform() { let url = URL(string: "https://mustachify.app/")! var request = URLRequest(url: url) request.httpMethod = "POST" request.httpBody = self.sourceImageData URLSession.shared.dataTask(with: request) { (data, _, error) in guard error == nil else { self.activityDidFinish(false) return } if let data = data, let image = UIImage(data: data) { self.mustachioedImage = image self.activityDidFinish(true) } else { self.activityDidFinish(false) } } } |
Show results
The final step is to provide a view controller to display our performance.
The QuickLook framework provides a simple, built-in way of displaying images. We’ll extend our activity to apply the QLPreviewControllerDataSource and return a version of the QLPreviewController, with itself set as dataSource to override the theactivityViewController method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import QuickLook extension MustachifyActivity: QLPreviewControllerDataSource { override var activityViewController: UIViewController? { guard let image = self.mustachioedImage else { return nil } let viewController = QLPreviewController() viewController.dataSource = self return viewController } // MARK: QLPreviewControllerDataSource func numberOfPreviewItems(in controller: QLPreviewController) -> Int { return self.mustachioedImage != nil ? 1 : 0 } func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem { return self.mustachioedImage! } } |
Provide Custom Activity for Users
We can use our brand new mustache
operation by passing it to the applicationActictivity parameter in the UIActivityViewController constructor:
1 2 3 4 5 6 7 8 | let activityViewController = UIActivityViewController(activityItems: [image], applicationActivities: [Mustachify()]) present(activityViewController, animated: true) { … } |
Link: https://nshipster.com/uiactivityviewcontroller/#defining-a-custom-activity-type