I have these protocol methods,
activityViewControllerPlaceholderItem:
and activityViewController:itemForActivityType:
but they never get called. How do I tell the UIActivityViewController
to call them?
I also have an UIActivityItemProvider
subclass, however I'm confused as to who calls these 2 methods. I'd really appreciate some sample code, as I can't find anything on the web. :)
According to the documentation. The array of activity items that you pass to
-initWithActivityItems:applicationActivities:
can be an array of data objects, like strings or images, or it can be array of objects that implement the UIActivityItemSource
protocol.
If you pass an array of objects that implement the UIActivityItemSource
protocol then your instance of UIActivityViewController
will call those methods on your activity items. Those objects do not necessarily have to be subclasses of UIActivityItemProvider
. UIActivityItemProvider
is just a class that conforms to this protocol.
You can implement the protocol wherever you want, even your viewcontroller is fine. Just instantiate the activityViewController with initWithActivityItems:@[self]
.
The answer by JotWee helped me out.
There is no need for sub classing, UIActivityItemSource
protocol methods can be implemented in view controller where share button is implemented.
Very important to add self
in activity items array, like this (as JotWee suggested):
NSArray *activityItems = [NSArray arrayWithObjects:self, url, image, nil];
Here is my final implementation:
ViewController.h
@interface ViewController : UIViewController <UIActivityItemSource>
ViewController.m
- (void)shareBarButtonItemClick:(UIBarButtonItem *)sender
{
NSURL *url = [NSURL URLWithString:@"http://example.com"];
NSURL *imageUrl = [NSURL URLWithString:@"http://example.com/images/1.jpg"];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageUrl]];
NSArray *activityItems = [NSArray arrayWithObjects:self, url, image, nil];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
[self presentViewController:activityViewController animated:YES completion:nil];
}
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
return @"Summary Text";
}
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
return @"";
}
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType
{
return @"Subject";
}
Piggybagging off of what JotWee and Sihad Begovic had provided, here's the Swift 5.0 version of making your ViewController
adopts the UIActivityItemSource
protocol and using share
barButtonItem to trigger the sharing of objects with other apps on your iPhone(s), and or, iPad(s):
Inside the viewDidLoad
method of your ViewController
class, implement the following:
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(shareBarButtonItemClicked(_:)))
And somewhere outside your viewDidLoad
method, but inside your ViewController
class, let us implement the shareBarButtonItemClicked
method as below:
@objc func shareBarButtonItemClicked(_ sender: UIBarButtonItem) {
//Here, iOS requires that you use self for your array of items
let items = [self]
let activityVC = UIActivityViewController(activityItems: items, applicationActivities: nil)
//App activities to be excluded from sharing to
activityVC.excludedActivityTypes = [
UIActivityType.airDrop,
UIActivityType.print,
UIActivityType.saveToCameraRoll,
UIActivityType.addToReadingList
]
if UIDevice.current.userInterfaceIdiom == .pad {
if activityVC.responds(to: #selector(getter: UIViewController.popoverPresentationController)) {
activityVC.popoverPresentationController?.barButtonItem = sender
}
}
self.present(activityVC, animated: true, completion: nil)
}
Now here's where things gets interesting. We need to make your ViewController
class conform to the UIActivityItemSource
protocol and implement its required methods as below:
extension ReferAFriendViewController: UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return String(describing: "Sharing my awesome app")
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
return String(describing: "Sharing my awesome app")
}
// Now you can modify and use the same above method if you want different messages/info to be presented for different apps user selects to share with.
// The Secret is utilizing the activityType parameter.
// You just have to uncomment below method and swap it with above method.
/*
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
if activityType == .postToTwitter {
return "Download #MyAwesomeApp via @vickSwift."
} else if activityType == .mail {
return String(describing: "My awesome app is lit. So download it")
} else {
return String(describing: "Download my awesome app")
}
}
*/
// Now note that when you share your app via a mail app, you might need to provide `subject` line.
// In fact, the `UIActivityItemSource` protocol provide us an optional delegate method to accomplish just that; implemented below:
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivityType?) -> String {
return String(describing: "My Awesome app email's subject title")
}
}
And that's how you adopt your class to conform to the UIActivityItemSource protocol and implement its methods. For more readings, I find this web article from HackingWithSwift website very really insightful.
Happy coding!