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)
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 to share a webview url on iOS 11 and when using Copy then go to Messages app then paste, it is pasted twice.
Out of curiosity I tried to do the same with Safari, Chrome, Firefox, and also for other apps than messages. The result was interesting:
Coping from Safari always works in any app
Coping from Chrome or FireFox works in Notes app, but it duplicates the copied text in any app that has TextField (Messages, WhatsApp, Slack, Signal,...)
Here is my simple code
func shareURL(title: String, url: URL) {
var activityItems = [AnyObject]()
activityItems.append(TitleActivityItemProvider(title: title))
activityItems.append(url as AnyObject)
let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
}
and here is the TitleActivityItemProvider class
class TitleActivityItemProvider: UIActivityItemProvider {
static let activityTypesToIgnore = [UIActivityType.copyToPasteboard]
init(title: String) {
super.init(placeholderItem: title)
}
override var item : Any {
if let activityType = activityType {
if TitleActivityItemProvider.activityTypesToIgnore.contains(activityType) {
return NSNull()
}
}
return placeholderItem! as AnyObject
}
override func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivityType?) -> String {
return placeholderItem as! String
}
}
is it a bug on iOS 11 or should I consider doing specific change when working with UIActivityViewController
==UPDATE==
I noticed when I comment adding TitleActivityItemProvider it works fine and when I add it, it duplicates the url however I ignore UIActivityType.copyToPasteboard in the title provider and return NSNull()
So I fixed it by using url.absoluteString when adding item to activityItems
activityItems.append(url.absoluteString as AnyObject)
Are you testing on the Simulator? On the simulator I had this issue, but on device it only pastes once.
My app is not Live yet. I got the app ID from the App Store Connect. I want to share the app link on social media apps. I used the UIActivityViewController:
let string1 = "itms-apps://itunes.apple.com/app/idXXXXXXX"
let url = NSURL(string: string1)
let shareItems = [UIApplication.sharedApplication().openURL(url!)]
let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
self.presentViewController(activityViewController, animated: true, completion: nil)
Problem: It is not showing some social media apps like WhatsApp.
Solution for Swift 4 or better:
This solution also works on iPad (the solution above crashes on iPad):
if let urlStr = NSURL(string: "https://apps.apple.com/us/app/idxxxxxxxx?ls=1&mt=8") {
let objectsToShare = [urlStr]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
if UIDevice.current.userInterfaceIdiom == .pad {
if let popup = activityVC.popoverPresentationController {
popup.sourceView = self.view
popup.sourceRect = CGRect(x: self.view.frame.size.width / 2, y: self.view.frame.size.height / 4, width: 0, height: 0)
}
}
self.present(activityVC, animated: true, completion: nil)
}
This is used to open the site, not to share the app:
[UIApplication.sharedApplication().openURL(url!)]
Do this instead:
if let name = URL(string: "https://itunes.apple.com/us/app/myapp/idxxxxxxxx?ls=1&mt=8"), !name.absoluteString.isEmpty {
let objectsToShare = [name]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.present(activityVC, animated: true, completion: nil)
} else {
// show alert for not available
}
for sample see this
The solutions here are all good but it's worth considering implementing the UIActivityItemSource protocol and LinkPresentation framework.
My solution achieves the following:
App icon and title showing at the top of the UIActivityViewController
Direct link to App Store for AirDrop ActivityType
Custom text for messages and emails, including an opportunity to add a link to the app on Google Play if required
Subject for emails
Doesn't use the LPMetaDataProvider fetch request (as described in this WWDC 2019 262 video) so faster to load
0. Init the UIActivityViewController:
Set the items to self:
let activityVC = UIActivityViewController(activityItems: [self], applicationActivities: nil)
Exclude certain ActivityTypes which don't apply:
activityVC.excludedActivityTypes = [.addToReadingList, .assignToContact, .markupAsPDF, .openInIBooks, .saveToCameraRoll]
For iPad set the popoverPresentationController.sourceView or .barButtonItem (this is ignored on iPhone):
activityVC.popoverPresentationController?.sourceView = myButton
Present it:
present(activityVC, animated: true, completion: nil)
1. Implement the required UIActivityItemSource methods
https://developer.apple.com/documentation/uikit/uiactivityitemsource
You must implement the placeholder method which according to the docs:
Placeholder objects do not have to contain any real data but should be configured as closely as possible to the actual data object you intend to provide.
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return ""
}
And the actual data, returning a link to the app for AirDrop and text for everything else:
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
if activityType == .airDrop {
return URL(string: "APP_STORE_URL")!
}
return "Check out the APP_NAME on the App Store: APP_STORE_URL or on the Google Play Store: PLAY_STORE_URL"
}
2. Implement the subject method
From the docs:
For activities that support a subject field, returns the subject for the item.
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String {
return "EMAIL_SUBJECT" // e.g. App name
}
3. Implement the LPLinkMetaData method
From the docs:
Returns metadata to display in the preview header of the share sheet.
#available(iOS 13.0, *)
func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
let metadata = LPLinkMetadata()
metadata.title = "APP_NAME"
return metadata
}
iPhone solution for Swift 5+
let url = URL(string: "https://apps.apple.com/us/app/id1535629801")!
let vc = UIActivityViewController(activityItems: [url], applicationActivities: nil)
present(vc, animated: true)
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.