UIActivityViewController not sharing images - ios

The below code intends to share different text and different images (UIImage and NSData) depending on the different platform selected. The trouble is that none of the images are being passed from activityImageShare() to the UIActivityViewController.
Question:
What’s could be wrong here exactly?
How can I correct the code below to ensure that activityImageShare()
shares the images, and ensure they are picked up as an activityType
by UIActivityViewController?
Code:
1 - Four images, three UIImage and one NSData.
var myImage1: UIImage!
var myImage2: UIImage!
var myImage3: NSData!
var myImage4: UIImage!
2 - Define images based on platform selected to pass to UIActivityViewController.
class activityImageShare: NSObject, UIActivityItemSource {
#objc func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject {
return ""
}
#objc func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
switch activityType {
case UIActivityTypePostToFacebook:
return myImage1
case UIActivityTypePostToTwitter:
return myImage2
case UIActivityTypePostToWeibo:
return myImage3
default:
return myImage4
}
}
}
3 - Define text based on platform selected to pass to UIActivityViewController.
class activityTextShare: NSObject, UIActivityItemSource {
#objc func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject {
return ""
}
#objc func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
switch activityType {
case UIActivityTypePostToFacebook:
return "This is my text 1."
case UIActivityTypePostToTwitter:
return "This is my text 2."
case UIActivityTypePostToWeibo:
return "This is my text 3."
default:
return "This is my text 4."
}
}
}
4 - Present UIActivityViewController based on text and images defined in activityTextShare() and activityImageShare().
func myShareActivity() {
let activity = UIActivityViewController(activityItems: [activityTextShare(), activityImageShare()], applicationActivities: nil)
self.presentViewController(activity, animated: true, completion: nil)
}

Try return placeholder image in activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) instead of ""

Related

Removing activity items from UIActivityViewController after choose WhatsApp

I am implementing UIActivityViewController to share text and image through other apps. My problem starts when WhatsApp doesn't accept text and image together, so I want to remove text (if exists) when user chooses to share with WhatsApp.
How can I remove some activity items after the destination app was choosen in UIActivityViewController?
First of all, you need to create a class that conforms to UIActivityItemSource, and use it instead of passing the text or image directly to UIActivityViewController.
So instead of
UIActivityViewController(activityItems: [image, text])
We would pass the new item sources
UIActivityViewController(activityItems: [ImageItemSource(image), TextItemSource(text)]
The item source classes will look something like this:
class TextItemSource: NSObject, UIActivityItemSource {
private let text: String
init(text: String) {
self.text = text
super.init()
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
text
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
text
}
}
class ImageItemSource: NSObject, UIActivityItemSource {
private let image: UIImage
init(image: UIImage) {
self.image = image
super.init()
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
image
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
image
}
}
Now that we have control over what we'll share, we can choose what exactly we share with each app by checking activityType before returning the data, and if we think we should not send anything at all, we can simply return nil.
In your case, if you want to share the text item with all apps except WhatsApp, you can simply do this:
class TextItemSource: NSObject, UIActivityItemSource {
...
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
if activityType?.rawValue.starts(with: "net.whatsapp.WhatsApp.") == true {
// We'll return `nil` to WhatsApp forcing it to
// ignore this whole text item and only show the image item instead
return nil
}
return text
}
...
}

How can you customize UIActivityViewController to ONLY include custom UIActivity types?

I'm trying to customize the native ios UIActivityViewController so it will restrict every UIActivity except for the custom UIActivity types that I initialize the UIActivityViewController with.
Essentially, I only want the user to see my four custom UIActivitys.
let items = ["Hello world"]
let activitySheet = CustomUIActivityViewController(
activityItems: items,
applicationActivities: [
CustomMailUIActivity(),
CustomMessagesUIActivity(),
CustomTwitterUIActivity(),
CustomFacebookUIActivity()
]
)
I understand that you can use activitySheet.excludedActivityTypes = [] to exclude types that you do not need, however you are not able to exclude third party applications such as Slack.
I was wondering if there was any way to get around that and ONLY include custom applicationActivies. It would also be great to remove the "More" button that appears on the share sheet as well. Any help would be greatly appreciated.
This is what I'm trying to achieve.
screenshot
My colleague was able to figure this out. For anyone wondering how to display only the applicationActivities (custom activities) on the share sheet, this is how I did it.
For activityItems rather than creating an array of strings, create an array of your custom data objects. Then override the canPerform(withActivityItems activityItems: [Any]) method in your custom UIActivity (subclass of UIActivity) to return true if activityItems[o] is CustomItem. Since your activityItems are custom, system will not display other apps on the share sheet. That's it.
In the example below, only your CustomUIActivity will be displayed on the share sheet.
let items = [CustomItem("Hello world")]
let activitySheet = UIActivityViewController(
activityItems: items,
applicationActivities: [
CustomUIActivity()
]
)
class CustomItem {
let value: String
required init(value: String) {
self.value = value
}
func getValue() -> String {
return self.value
}
}
#objc(CustomUIActivity)
class CustomUIActivity: UIActivity {
override class var activityCategory: UIActivity.Category {
return .share
}
override var activityType: UIActivity.ActivityType? {
return .customuiactivity
}
override var activityTitle: String? {
return "Custom"
}
override var activityImage: UIImage? {
return UIImage(named: "custom-icon")
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
if activityItems.count == 1 && activityItems[0] is CustomItem {
return true
}
return false
}
var textToShare: String?
override func prepare(withActivityItems activityItems: [Any]) {
if let activityItem = activityItems.first as? CustomItem {
self.textToShare = activityItem.getValue
}
}
override func perform() {
// perform your custom activity with `textToShare`
activityDidFinish(true)
}
}
extension UIActivity.ActivityType {
static let customuiactivity =
UIActivity.ActivityType("com.ceylonese.mobile.customuiactivity")
}

Share image with hashtag via UIActivityViewController (Twitter, Facebook, Instagram)

I am attempting to share an image with a hashtag using UIActivityViewController and I am encountering some strange behavior when attempting to share to Twitter, Facebook and Instagram. There does not seem to be a lot of documentation about these service's share extensions.
Scenario 1: Init controller with activity item array with image and text
If I initialize the controller like so, Twitter and Facebook will show up in the controller (no Instagram as it does not support text items), and both will programmatically pre-populate the hashtag in the text entry field:
let activityVC = UIActivityViewController(activityItems: [myHashtagString, myImage], applicationActivities: nil)
Scenario 2: Init controller with only image
In this scenario, all networks show up, but I (obviously) lose the automatic hashtag feature:
let activityVC = UIActivityViewController(activityItems: [myImage], applicationActivities: nil)
Scenario 3: UIActivityItemSource subclass
If I make my own UIActivityItemSource subclass, I can almost get everything to work. However, and this is what I cannot figure out, using the protocol methods as I have below results in the automatic hashtag working for Facebook, but not Twitter. How can this be possible -- is there a special key needed for Twitter? There must be a way for it to work if it works in Scenario #1...
Interestingly, this method works for both Twitter and Facebook if I insert a URL (commented out). So why on earth won't the text work for Twitter!?
let activityItem = CustomItemSource(image: image, message: "#TestTag")
let activityVC = UIActivityViewController(activityItems: [activityItem], applicationActivities: nil)
...
class CustomItemSource: NSObject, UIActivityItemSource {
private var image: UIImage!
private var message: String!
// MARK: Init
init(image: UIImage, message: String) {
super.init()
self.image = image
self.message = message
}
// MARK: Item Source Protocol
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return image
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
if activityType == .postToTwitter || activityType == .postToFacebook {
//return ["url": URL(string: "https://www.google.com")!, "image": image]
return ["text": message, "image": image]
}
else {
return ["image": image]
}
}
}
Define two UIActivityItemSource classes, one for Image and one for Text.
In first one only return the image.
In second one return NSObject() for placeHolder, and return Text or nil depending on activity. By returning NSObject(), UIActivity will allow all services to be available.
UIActivityViewController(activityItems: [ImageProvider(), TextProvider()], applicationActivities: nil)
and providers:
class TextProvider: NSObject, UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return NSObject()
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
if activityType == .postToTwitter || activityType == .postToFacebook {
return "Tweet with #Hashtag"
}
return nil
}
}
class ImageProvider: NSObject, UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return UIImage(named: ...)
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
return UIImage(named: ...)
}
}
Explaination
First of all, Keys are not really sensitive, The only sensitive-key was "subject" for email and apps supporting it, which is implemented in UIActivityController's API and we can set it directly. It doesn't matter if you provide UIImage with key "image" or "1".
As it turns out, Twitter activity will not work if it's text is not returned directly in ...itemForActivity... method. So the solution is to separate item sources.
Twitter activity also will not work, if placeholder receives anything other than String, but by returning String Instagram activity will not work, So by returning NSObject() Type will be ignored and all services will be available.
if you want to limit some services use UIActivityViewController.excludedActivityTypes

Sending custom data via UIActivityViewController

Im trying to send data (NSData) from my app on one iOS Device to another via AirDrop using the UIActivityViewController. I have created a new CSM (custom data type) in my apps plist. The public.filename-extension = ppm.
So how do I add the ppm extension to the NSDate object I'm trying to send ?? Am I right in thinking that when a you present a UIActivityViewController, my apps Icon will not be displayed in the UIActivityViewController window if the object Im sending does not have my apps public extension (ppm) ??.... yea, I'm really confused !!
Heres the code I'm using to present UIActivityViewController
#IBAction func shareButton(sender: AnyObject) {
// myData is the object I want to send to be used in my app on another device
let vc = UIActivityViewController(activityItems: [myData],applicationActivities: [])
presentViewController(vc, animated: true, completion: nil)
}
Basically, all I'm trying to do is send custom data to be used in my app
You should take a look at the AirDrop sample code that covers the case of defining your own file type and sharing that with your app on the other device. The key part if you want to share raw data is that you have to create an instance of UIActivityItemSource and pass that to UIActivityViewController. Something like this:
class DataActivityItemSource: NSObject, UIActivityItemSource {
let myData: NSData
let typeIdentifier: String
let subject: String
let previewImage: UIImage
init(myData: NSData, typeIdentifier: String, subject: String, previewImage: UIImage) {
self.myData = myData
self.typeIdentifier = typeIdentifier
self.subject = subject
self.previewImage = previewImage
}
// called to determine data type. only the class of the return type is consulted. it should match what -itemForActivityType: returns later
#objc func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject {
return myData
}
// called to fetch data after an activity is selected. you can return nil.
#objc func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
return myData
}
// if activity supports a Subject field. iOS 7.0
#objc func activityViewController(activityViewController: UIActivityViewController, subjectForActivityType activityType: String?) -> String {
return subject
}
// UTI for item if it is an NSData. iOS 7.0. will be called with nil activity and then selected activity
#objc func activityViewController(activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: String?) -> String {
return typeIdentifier
}
// if activity supports preview image. iOS 7.0
#objc func activityViewController(activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: String?, suggestedSize size: CGSize) -> UIImage? {
// look at suggestedSize and resize image (see AirDrop sample code for how to do this)
return previewImage
}
}
#IBAction func shareButton(sender: AnyObject) {
// myData is the object I want to send to be used in my app on another device
let itemSource = DataActivityItemSource(myData, "com.foo.ppm.typeIdentifier", "My Amazing Journey", aPreviewImage)
let vc = UIActivityViewController(activityItems: [itemSource],applicationActivities: [])
presentViewController(vc, animated: true, completion: nil)
}

How to share different picture per platform in Activity View Controller?

I am making an app using native Swift where a user can share pre made images using an Activity View Controller (AVC). The images are pretty high resolution at 1024x1024 but when these are forwarded to "Messages" on iOS7 the tops and bottoms get cut off. This does happen when the image is 128x128 or if sharing any resolution on iOS8.
Since I feed the image in as PNGDataRepresentation before showing the AVC I was wondering if there is a way to feed in a different image based on what platform the user selected. I'd basically point to a lower resolution asset if the user selects Messages vs high resolution if selecting Save or Facebook share.
func openShareWidget ()
{
var activityProvider: ShareActivityProvider = ShareActivityProvider();
activityProvider.currentType = self.currentType;
var imageData : NSData = UIImagePNGRepresentation(selectedCell.getSharingImage());
var activityItems : NSArray = [activityProvider, imageData];
var activityController : SharingActivityViewController = SharingActivityViewController(activityItems: activityItems, applicationActivities: []);
presentViewController(activityController, animated: true, completion: nil);
}
My ActivityProvider extension implementation below:
class ShareActivityProvider: UIActivityItemProvider, UIActivityItemSource {
var currentType : ImageType!;
override func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject?
{
switch activityType
{
case UIActivityTypeMail:
return "" ;
case UIActivityTypeMessage:
return "";
case UIActivityTypePostToFacebook:
return "Facebook"
case UIActivityTypePostToTwitter:
return "Twitter";
default:
return "";
}
}
override func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject
{
return "";
}
}
I've gotten this to work by using multiple UIActivityItem providers. One supplies the text for the message body whereas the 2nd supplies the image itself.
func openShareWidget ()
{
var textProvider : ShareTextActivityProvider = ShareTextActivityProvider(); // same as ShareActivityProvider from question
var imgProvider : ShareImageActivityProvider = ShareImageActivityProvider()
var activityItems : NSArray = [textProvider, imgProvider];
var activityController : SharingActivityViewController = SharingActivityViewController(activityItems: activityItems, applicationActivities: []);
presentViewController(activityController, animated: true, completion: nil);
}
And here's the ShareImageActivityProvider
class ShareImageActivityProvider: UIActivityItemProvider {
var selectedCell : ImageCell!;
override func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject?
{
return UIImagePNGRepresentation(selectedCell.getSharingImage(activityType));
}
override func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject
{
return "";
}
}
GetSharingImage:
func getSharingImage(activityType : String) -> UIImage
{
switch activityType
{
case UIActivityTypeMessage:
return UIImage(named: imgPathLOW);
case UIActivityTypePostToFacebook, UIActivityTypeMail, UIActivityTypePostToTwitter:
return UIImage(named: imgPathHD);
default:
return UIImage(named: "Tex_Empty");
}
}

Resources