Give thumbnail image with UIActivityViewController - ios

I'm trying to share an image with text through UIActivityViewController. If I do this:
let activityVC = UIActivityViewController(activityItems: [text, image], applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
Everything works fine. The problem is that I only want to share the image with certain activity types. i.e. when a user shares to Facebook I don't want to have an image, for everything else I do though. My problem is this stupid method is never called:
optional func activityViewController(_ activityViewController: UIActivityViewController,
thumbnailImageForActivityType activityType: String?,
suggestedSize size: CGSize) -> UIImage?
Which should be becuase it's defined in UIActivityItemSource protocol. Is there any work around to this?
So I believe to have made some headway here. Turns our if you pass multiple values of self when instantiating UIActivityViewController you can return multiple values in the itemForActivityType delegate method. So if I do this:
let activityVC = UIActivityViewController(activityItems: [self, self], applicationActivities: nil)
I can return different values like this:
func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
if activityType == UIActivityTypePostToFacebook {
return ["hello", "world"]
}
else {
return ["goodnight", "moon"]
}
}
However, it seems that you can only return two values of the same type.
My new question is now, how would I return both an image and text?? The hunt continues...

In order to share two different set of content you have to create two different itemsource
we can set different text content for different activity type.Add the MyStringItemSource class to your viewcontroller
SourceOne:
class MyStringItemSource: NSObject, UIActivityItemSource {
#objc func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject {
return ""
}
#objc func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
//You can pass different text for for diffrent activity type
if activityType == UIActivityTypePostToFacebook {
return "String for facebook"
}else{
return "String for Other"
}
}
}
Our requirement is to add image to all activity type except FB,to do that add the MyImageItemSource class in your VC.
SourceTwo:
class MyImageItemSource: NSObject, UIActivityItemSource {
#objc func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject {
return ""
}
#objc func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
//This one allows us to share image ecxept UIActivityTypePostToFacebook
if activityType == UIActivityTypePostToFacebook {
return nil
}
let Image: UIImage = UIImage(data: NSData(contentsOfURL: NSURL(string: "https://pbs.twimg.com/profile_images/604644048/sign051.gif")!)!)!
return Image
}
}
Now we are ready to set UIActivityViewController,here we go
#IBAction func Test(sender: AnyObject) {
let activityVC = UIActivityViewController(activityItems: [MyStringItemSource(),MyImageItemSource()] as [AnyObject], applicationActivities: nil)
//Instead of using rootviewcontroller go with your own way.
if let window = (UIApplication.sharedApplication().delegate as? AppDelegate)?.window
{
window.rootViewController?.presentViewController(activityVC, animated: true, completion: nil)
}
}
TWITTER Shared Dialogue:
Contains image and given text
FB Share Dialogue:
Contains only the given text

Related

Open in Safari with UIActivityViewController?

I'm sharing a URL via UIActivityViewController. I'd like to see "Open in Safari" or "Open in browser" appear on the share sheet, but it doesn't. Is there a way to make this happen?
Note: I am not interested in solutions that involve adding somebody else's library to my app. I want to understand how to do this, not just get it to happen. Thanks.
Frank
Yes, you could add your custom action to Share sheet in iOS
You would have to copy this class.
class MyActivity: UIActivity {
var _activityTitle: String
var _activityImage: UIImage?
var activityItems = [Any]()
var action: ([Any]) -> Void
init(title: String, image: UIImage?, performAction: #escaping ([Any]) -> Void) {
_activityTitle = title
_activityImage = image
action = performAction
super.init()
}
override var activityTitle: String? {
return _activityTitle
}
override var activityImage: UIImage? {
return _activityImage
}
override var activityType: UIActivity.ActivityType {
return UIActivity.ActivityType(rawValue: "com.someUnique.identifier")
}
override class var activityCategory: UIActivity.Category {
return .action
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
return true
}
override func prepare(withActivityItems activityItems: [Any]) {
self.activityItems = activityItems
}
override func perform() {
action(activityItems)
activityDidFinish(true)
}
}
Please go through the class you might need to change a few things.
This is how you use it.
let customItem = MyActivity(title: "Open in Safari", image: UIImage(systemName: "safari") ) { sharedItems in
guard let url = sharedItems[0] as? URL else { return }
UIApplication.shared.open(url)
}
let items = [URL(string: "https://www.apple.com")!]
let ac = UIActivityViewController(activityItems: items, applicationActivities: [customItem])
ac.excludedActivityTypes = [.postToFacebook]
present(ac, animated: true)
I have done this for one action, and tested it, it works.
Similarly you could do it for other custom actions.
For more on it refer this link.
Link To Detailed Post

share view controller with uiactivityviewcontroller

I want to add a share button to my app. Can I share a complete view controller so that when a user clicks on the shared link it comes to the view controller that was shared?
So far I have only found things like how to share images and text.
This is my code:
#IBAction func shareButtonTapped(_ sender: UIBarButtonItem) {
let items = [self]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
}
At the moment I can only share a string.
extension DetailViewController: UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return "Test"
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return "Test"
}
}

iOS: Sharing via UIActivityViewController does not work with UIActivityItemSource

I wanted to further customize how content is shared from my app and instead of providing UIActivityViewController with image and title use the UIActivityItemSource protocol to implement methods that provide this content.
So I created extension for my model Scan like this:
extension Scan: UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return self.title as Any
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return self.orderedDocuments.map({ $0.image }) as Any
}
func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? {
return self.thumbnailImage
}
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String {
return "Scan from: \(String(describing: self.created))"
}
}
But when I pass instance of Scan to the UIActivityViewController it opens but it is empty. I cannot see the title, preview or the actual images.. These methods are getting called.
I am presenting the UIActivityViewController like this as there is not much to customize:
func share(_ scan: Scan) {
let shareController = UIActivityViewController(activityItems: [scan], applicationActivities: nil)
present(shareController, animated: true)
}
The Scan is Core Data entity but I tried creating separate class just for sharing and that did not work either.
EDIT: So I got some progress with metadata like this:
func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
guard let image = self.orderedDocuments.first?.image else { return nil }
let imageProvider = NSItemProvider(object: image)
let metadata = LPLinkMetadata()
metadata.imageProvider = imageProvider
metadata.title = title
return metadata
}
It at least shows proper title and thumbnail. I have seen some apps even displaying subtitle and file size which I guess is not possible for in-memory images?
I still have issues with sharing more than one image.
My updated activity controller methods:
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return self.orderedDocuments.first?.image
}
func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String {
return kUTTypePNG as String
}
Regarding:
I have seen some apps even displaying subtitle and file size which I
guess is not possible for in-memory images?
It's silly, but can be done with:
linkMetaData.originalURL = URL(fileURLWithPath: "Insert whatever info here")
For example, for a result like this (custom thumbnail, custom title and subtitle with file size):
... the code is:
func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
let linkMetaData = LPLinkMetadata()
// Thumbnail
linkMetaData.iconProvider = NSItemProvider(object: someValidUIImage)
// Title
linkMetaData.title = "Ohay title!"
// Subtitle with file size
let prefix = "PNG Image"
let fileSizeResourceValue = try! someValidURL.resourceValues(forKeys: [.fileSizeKey])
let fileSizeInt = fileSizeResourceValue.fileSize
let byteCountFormatter = ByteCountFormatter()
byteCountFormatter.countStyle = .file
let fileSizeString = byteCountFormatter.string(fromByteCount: Int64(fileSizeInt))
let suffix = "・\(fileSizeString)"
//👇
linkMetaData.originalURL = URL(fileURLWithPath: "\(prefix)\(suffix)")
return linkMetaData
}

UIActivityViewController Hide my application share Extension

In my application, I have added my share extension it's working fine, But I face One problem When I invite app through UIActivityViewController I show my application Extension. How can I hide my application Extension of my own application?
You can do it by adding your Extension activity type in the list of excluded activity types:
let activityViewController = UIActivityViewController(activityItems: <your items>, applicationActivities: <your supported application activities>)
let extensionActivityType = UIActivityType(<your extension activity type id>)
activityViewController.excludedActivityTypes = [extensionActivityType]
Sorry for late but hope this ans will surly help you out.
First of all define below lines in your code
class ActionExtensionBlockerItem: NSObject, UIActivityItemSource {
func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivityType?) -> String {
return "com.your.unique.uti";
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
// Returning an NSObject here is safest, because otherwise it is possible for the activity item to actually be shared!
return NSObject()
}
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivityType?) -> String {
return ""
}
func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivityType?, suggestedSize size: CGSize) -> UIImage? {
return nil
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return ""
}
}
Here com.your.unique.uti is your Application group identifier
and then while presenting activityViewController use code below
let activityViewController = UIActivityViewController(activityItems: [/* Other Items To Share, */ ActionExtensionBlockerItem()], applicationActivities: nil)

Custom message fetched from API in UIActivityViewController

I want my app users to share an link using an UIACtivityViewController. The link is not static and I have to fetch it from an api. I want to fetch this link as soon as someone presses a icon in UIActivityViewController but how I am doing it now is not working. The completionhandler of the request is always excuted at the end of the method fetchShareURL(). Could someone tell me how to fix this?
import UIKit
class ShareItemSource: NSObject, UIActivityItemSource {
var objects : [AnyObject]?
var shareURL : String?
init(objects : [AnyObject]?){
self.objects = objects
}
func fetchShareURL() -> Bool{
//fetch share url from api
let api = Api()
let semaphore = dispatch_semaphore_create(0);
let backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
dispatch_async(backgroundQueue, {
api.export(self.groupObjList, receiptObjList: self.receiptObjList){ status, message, url in
dispatch_async(dispatch_get_main_queue(), {
self.shareURL = url
dispatch_semaphore_signal(semaphore);
})
}
});
let delayInSeconds = 30.0;
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
dispatch_semaphore_wait(semaphore, delayTime)
if self.shareURL != nil{
return true
} else {
return false
}
}
#objc func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject {
return ""
}
#objc func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
if fetchShareURL() == true{
return "Download files: "+self.shareURL!
} else {
return nil
}
}
func activityViewController(activityViewController: UIActivityViewController, subjectForActivityType activityType: String?) -> String {
return "title of share"
}
func activityViewController(activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: String?, suggestedSize size: CGSize) -> UIImage? {
//do stuff
}
}
If data to be provided in a UIActivityView takes time to provide, use a UIActivityItemProvider. It is an NSOperation subclass and solves the whole problem in a completely coherent way. Indeed, this sort of situation is exactly what it is intended for. (And watch the WWDC 2015 video on advanced NSOperation, please.)
Changed it to this for now. Later I'll use an NSOperation.
api.export(self.objects){ status, message, url in
if(status == true){
if let url = url{
alert.dismissViewControllerAnimated(true){
let sharingItems = ["Share url", url]
let activityViewController = UIActivityViewController(activityItems: [ShareItemSource(url: url)], applicationActivities: nil)
activityViewController.excludedActivityTypes = [UIActivityTypeAddToReadingList, UIActivityTypeCopyToPasteboard]
self.presentViewController(activityViewController, animated: true, completion: nil)
activityViewController.completionWithItemsHandler = { (activity, success, items, error) in
//Response
}
}
}
} else {
//handle fail
}
}

Resources