I want to bring out the share sheet with the AirDrop as the only option in my application.
In iOS 7, I could simple add all share types to the excludedActivityTypes property of UIActivityViewController (UIActivityTypeMessage, UIActivityTypeMail, ...)
But now in iOS 8, with the app extensions, the users may have extra sharing providers that show up.
Is there any way to show AirDrop only?
UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:#[[NSURL URLWithString:url]]
applicationActivities:nil];
NSArray *excludedActivities = #[UIActivityTypePostToTwitter, UIActivityTypePostToFacebook,
UIActivityTypePostToWeibo,
UIActivityTypeMessage, UIActivityTypeMail,
UIActivityTypePrint, UIActivityTypeCopyToPasteboard,
UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll,
UIActivityTypeAddToReadingList, UIActivityTypePostToFlickr,
UIActivityTypePostToVimeo, UIActivityTypePostToTencentWeibo];
class AirDropOnlyActivityItemSource : NSObject, UIActivityItemSource {
/// The item you want to send via AirDrop.
let item: AnyObject
init(item: AnyObject) {
self.item = item
}
func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject {
/// Use an empty URL as placeholder to let iOS Share Sheet show the AirDrop only.
return NSURL(string: "")!
}
func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
return item
}
}
Swift 4.2 version of Jonny's answer:
class AirDropOnlyActivityItemSource: NSObject, UIActivityItemSource {
///The item you want to send via AirDrop.
let item: Any
init(item: Any) {
self.item = item
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
//using NSURL here, since URL with an empty string would crash
return NSURL(string: "")!
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return item
}
}
Usage:
let itemSource = AirDropOnlyActivityItemSource(item: "test")
let activityVc = UIActivityViewController(activityItems: [itemSource], applicationActivities: nil)
That's currently not possible. You should file an enhancement request with apple explaining why this would be useful to the user/developer.
Related
I'm trying to share my app using UIActivityViewController but I can't reproduce the same effect as when I share an app from the App Store, meaning :
When I clicked the share button in the App Store I have something that looks like this :
But when I try to share my app I have this :
The code that I used was :
if let logo = UIImage(named: "myLogo"), let websiteURL = URL(string: "https://itunes.apple.com/app/idxxxxxxxxxx") {
let objectsToShare = ["My App Name", websiteURL, logo] as [Any]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: [])
if let popoverController = activityVC.popoverPresentationController {
popoverController.sourceView = self.view
popoverController.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
}
present(activityVC, animated: true)
}
The if let popoverController = ... loop is for preventing a crash when using iPads.
What do I have to change in order to have the effect as the App Store? (to have an image with a title and a subtitle)
Moreover, once I share the app with Messages for instance, this is the difference :
How can I have the same effect? (A single image with the title and subtitle, an as a bonus, a video). I'm not sure if this is an iOS 13 problem, since all similar questions don't have the same app sharing popover.
You have to use the new LinkPresentation framework.
Which essentially involves UIActivityItemSource conformance then retrieving the metadata that will encompass the Activity View and the data you are sharing. Data can be retrieved locally or downloaded.
ExampleController: UIViewController {
var metadata: LPLinkMetadata?
func share() {
let activityView = UIActivityViewController(activityItems: [self], applicationActivities: nil)
present(activityView, animated: true)
}
...
}
extension ExampleController: UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return metadata
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return metadata
}
func activityViewControllerLinkMetadata(_: UIActivityViewController) -> LPLinkMetadata? {
metadata = LPLinkMetadata()
metadata.title = "Title"
metadata.originalURL = URL(string: "Description")
metadata.url = metadata.originalURL
// Using a locally stored item
metadata.iconProvider = NSItemProvider(object: UIImage(named: "image")!)
metadata.imageProvider = NSItemProvider.init(contentsOf:
Bundle.main.url(forResource: "image", withExtension: "JPG"))
return metadata
}
}
Docs:
https://developer.apple.com/documentation/uikit/uiactivityitemsource/3144571-activityviewcontrollerlinkmetada
WWDC Presentation:
https://developer.apple.com/videos/play/wwdc2019/262/
activityViewController.popoverPresentationController?.sourceView = self.view // so that iPads won't crash
You can use this.
I am bringing up a share sheet for sharing a link on iOS13. By default, it will fetch the title and image at the top of the sheet from the shared URL, and I would like to overwrite both. I have the title ready, but the image is remote and needs to be fetched. Preferrably, I would like the share sheet with the title to show up instantly, and the remote image being loaded in afterwards.
I found out about a new method in UIActivityItemSource that seems to do this, so I created a custom UIActivityItemSource subclass:
#objc class CustomURLItemSource: NSObject, UIActivityItemSource {
...
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return shareURL
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return shareURL
}
#available(iOS 13.0, *)
func activityViewControllerLinkMetadata(_: UIActivityViewController) -> LPLinkMetadata? {
let metadata = LPLinkMetadata()
metadata.originalURL = shareURL
metadata.url = shareURL
metadata.title = "My custom title"
metadata.imageProvider = NSItemProvider(contentsOf: imageURL)
return metadata
}
}
Using this item source, my custom title will show up, but the image next to the title will just be the default safari icon. How does iOS expect me to provide the image or image url here?
In my case, in activityViewControllerLinkMetadata I added icon image as:
let image = UIImage(named: "app_icon")!
let imageProvider = NSItemProvider(object: image)
let metadata = LPLinkMetadata()
metadata.imageProvider = imageProvider
and after I presented it as:
let url = URL(string: "your_url_string")
let shareSheetVC = UIActivityViewController(activityItems: [url, self], applicationActivities: nil)
present(shareSheetVC, animated: true, completion: nil)
I am using UIActivityViewController for sharing contents from app.
I want to share different content with different sharing apps.
Like in Message output will be like this.image + text + URL
in Whatsapp i would like to share like below image text + URL
How can i do this? See below screen shots for this.
It took me a good time figuring this out, but that's how it worked for me:
Think of this problem in two steps: first, we need to tell to the UIActivityViewController which content we want to share. Second, we need to return the content based on each social media, either a link, an image or a text. Its up to the social media app to tell which content it can handle, and it will only show up if we share the right kind of content.
In the first step, we will try to fool the social media apps, saying that we want to share an UIImage and a NSObject. This will open most of the social media apps to share.
In the second step, we will identify which social media app the user has clicked, and return the appropriated content for it.
Implementation:
create two UIActivityItemSource, one which will return an UIImage and other which will return the NSObject.
class SocialActivityItem: NSObject, UIActivityItemSource {
var img: UIImage?
var url: URL?
convenience init(img: UIImage, url: URL) {
self.init()
self.img = img
self.url = url
}
// This will be called BEFORE showing the user the apps to share (first step)
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return img!
}
// This will be called AFTER the user has selected an app to share (second step)
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType?) -> Any? {
//Instagram
if activityType?.rawValue == "com.burbn.instagram.shareextension" {
return img!
} else {
return url
}
}
}
and
class TextActivityItem: NSObject, UIActivityItemSource {
var textToShare: String?
convenience init(textToShare: String) {
self.init()
self.textToShare = textToShare
}
// This will be called BEFORE showing the user the apps to share (first step)
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return NSObject()
}
// This will be called AFTER the user has selected an app to share (second step)
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType?) -> Any? {
var text = ""
if activityType?.rawValue == "net.whatsapp.WhatsApp.ShareExtension" {
text = "Sharing on Whatsapp"
}
if activityType == UIActivityType.postToFacebook {
text = "Sharing on Facebook"
}
return text
}
}
Then, you just need to set everything up:
let url = URL(string: "www.google.com")!
let socialProvider = SocialActivityItem(img: img, url: url)
let textProvider = TextActivityItem(textToShare: "Sharing on social media!")
let activityViewController = UIActivityViewController(activityItems: [socialProvider, textProvider], applicationActivities: nil)
I have the text in activity Items but now it share on WhatsApp or Facebook, this method share for each app the same text.
is it possible to share different text for each app ?
- (void)share {
NSString *text = #"share test";
NSArray * activityItems = #[[NSString stringWithFormat:#"%#", text], [NSURL URLWithString:#"http://www.test.co"]];
NSArray * applicationActivities = nil;
NSArray * excludeActivities = #[UIActivityTypeAssignToContact, UIActivityTypeCopyToPasteboard, UIActivityTypePostToWeibo, UIActivityTypePrint, UIActivityTypeMessage];
UIActivityViewController * activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities];
activityController.excludedActivityTypes = excludeActivities;
[self presentViewController:activityController animated:YES completion:nil];
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
NSLog(#"The setCompletionWithItemsHandler completed = %i", completed);
if (completed) {
NSLog(#"The selected activity was %#", activityType);
if ( [activityType isEqualToString:UIActivityTypeMail]) {
NSLog(#"Mail sended");
} else if ( [activityType isEqualToString:UIActivityTypePostToTwitter]) {
NSLog(#"Post on twitter, ok!");
} else if ( [activityType isEqualToString:UIActivityTypePostToFacebook]) {
NSLog(#"Post on facebook, ok!");
} else if ( [activityType isEqualToString:UIActivityTypeMessage]) {
NSLog(#"SMS sended!");
}
}
}];
}
Short answer: Yes!
There is actually a way to return different content for multiple apps, AFTER choosing them.
You will have to create two UIActivityItemSource, one which will return an UIImage and other which will return a NSObject. This is done to "fool" the system that you will share these objects, but after the user has selected an app, we can actually return something else, like a URL.
class SocialActivityItem: NSObject, UIActivityItemSource {
var img: UIImage?
var url: URL?
convenience init(img: UIImage, url: URL) {
self.init()
self.img = img
self.url = url
}
// This will be called BEFORE showing the user the apps to share (first step)
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return img!
}
// This will be called AFTER the user has selected an app to share (second step)
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType?) -> Any? {
//Instagram
if activityType?.rawValue == "com.burbn.instagram.shareextension" {
return img!
} else {
return url
}
}
}
and
class TextActivityItem: NSObject, UIActivityItemSource {
var textToShare: String?
convenience init(textToShare: String) {
self.init()
self.textToShare = textToShare
}
// This will be called BEFORE showing the user the apps to share (first step)
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return NSObject()
}
// This will be called AFTER the user has selected an app to share (second step)
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType?) -> Any? {
var text = ""
if activityType?.rawValue == "net.whatsapp.WhatsApp.ShareExtension" {
text = "Sharing on Whatsapp"
}
if activityType == UIActivityType.postToFacebook {
text = "Sharing on Facebook"
}
return text.isEmpty ? textToShare : text
}
}
Then, you just need to set everything up:
let url = URL(string: "www.google.com")!
let socialProvider = SocialActivityItem(img: img, url: url)
let textProvider = TextActivityItem(textToShare: "Sharing on social media!")
let activityViewController = UIActivityViewController(activityItems: [socialProvider, textProvider], applicationActivities: nil)
Short answer: No.
But you can use the open App with URL method and pass an parameter to that App.
When using that method you have to use a custom UI and can not use the UIActivityViewController. (As far as i know)
So, it is possible, if you want to have multiple buttons like:
"Share with WhatsApp"
"Share with Facebook"
etc.
I want to set subject for email sharing in UIActivityViewController and also want to share in Twitter. I know in Twitter if we want to share — we need compress text to 140 chars. I checked many SO solutions, but nothing is working.
Is this issue fixed in latest iOS releases? Any other "working solutions"?
Check below code for the email for setting up your email subject:
UIActivityViewController* avc = [[UIActivityViewController alloc] initWithActivityItems:#[#"Your String to share"]
applicationActivities:nil];
[avc setValue:#"Your email Subject" forKey:#"subject"];
avc.completionHandler = ^(NSString *activityType, BOOL completed) {
// ...
};
Here the line
[avc setValue:#"Your email Subject" forKey:#"subject"];
Makes the subject as "Your email Subject" if user picks email option in the UIActivityViewController.
I hope it helps...
It seems as though emreoktem's solution—sending setValue:forKey: to the UIActivityViewController—is undocumented.
On iOS 7 and later, you can implement the activityViewController:subjectForActivityType: method in an object conforming to the UIActivityItemSource protocol to do this in a way that is documented.
Here's a concrete solution for Swift 3.0+ based on the accepted answer. Note that, like the accepted answer, this is known to work only on the iOS Mail app and not necessarily other apps.
Implementation:
class MessageWithSubject: NSObject, UIActivityItemSource {
let subject:String
let message:String
init(subject: String, message: String) {
self.subject = subject
self.message = message
super.init()
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return message
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
return message
}
func activityViewController(_ activityViewController: UIActivityViewController,
subjectForActivityType activityType: UIActivityType?) -> String {
return subject
}
}
Usage:
Here's an example of usage. Note that it works well to use this as the first item in the activityItems array, and include any additional items to follow:
let message = MessageWithSubject(subject: "Here is the subject", message: "An introductory message")
let itemsToShare:[Any] = [ message, image, url, etc ]
let controller = UIActivityViewController(activityItems: itemsToShare, applicationActivities: nil)
For Swift 2.0+ & ios 8.0+
let title = "Title of the post"
let content = "Content of the post"
let objectsToShare = [title, content]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.setValue(title, forKey: "Subject")
self.presentViewController(activityVC, animated: true, completion: nil)