Unknown activity items supplied: ("<MyClass: somehash>") - ios

I am using subclass of UIActivityViewController to show custom UIActivities.
When Controller is showing, I am getting
Unknown activity items supplied: (
"<MyClass: somehash>"
)
in my logs.
MyClass subclasses NSObject and is passed to new ViewController invoked by tap on my UIActivity.
I tried to implement protocol UIActivityItemSource on MyClass, but it didn't solve the problem.
MyClass object is passed in array to [UIActivityViewController init] as activityItems parameter, and it shouldn't be a problem, as documentation says only:
The array of data objects on which to perform the activity. The type of objects in the array is variable and dependent on the data your application manages. For example, the data might consist of one or more string or image objects representing the currently selected content.
Edit:
This is the code I can share:
MyClass *myDataObject = some data...
...
NSMutableArray *activityItems = [NSMutableArray arrayWithCapacity:3];
[activityItems addObject:NSLocalizedString(#"default_activity_message", nil)];
[activityItems addObject:someURL];
[activityItems addObject:myDataObject];
NSMutableArray *customActivities = [NSMutableArray arrayWithCapacity:1];
[customActivities addObject:[MyActivity new]];
MyActivityViewController *activityViewController =
[[MyActivityViewController alloc] initWithActivityItems:activityItems
applicationActivities:customActivities];
[self presentViewController:activityViewController animated:YES completion:nil];

You should subclass UIActivity, not UIActivityViewController. And then pass in the subclass in applicationActivities:...
// Create activity view controller
UIActivityViewController* vc = [[UIActivityViewController alloc] initWithActivityItems:#[myDataItem] applicationActivities:#[myActivityAction]];

Related

IOS/Objective-C: UIActivityViewController: Customize for different Activity Types

I am trying to customize the content shared using the UIActivityViewController. The method suggested in some blog posts and answers on SO is to subclass UIActivityItemProvideras below. Some answers also say that the class should adopt a protocol UIActivityItemSource but I am fuzzy on what the protocol has to do with the Provider.
My code is in a view controller class which I have made accept the <UIActivityItemSource Protocol> in the .h file. I have also placed the following method in the .m file along with similar methods for message, placeholder, URL and thumbnail. However, they are not having any effect on the content shared which is the same for all the types, message, email, Facebook, etc.
What am I missing?
-(NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(UIActivityType)activityType
{
if (activityType == UIActivityTypeMessage) {
return #"My message subject";
} else if (activityType == UIActivityTypeMail) {
return #"My mail subject";
}
return #"My subject otherwise";
}
Edit:
The following code works fine to send identical content to different activities. But the above methods are evidently necessary to customize:
NSArray *activityItems = #[text,url];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems: activityItems applicationActivities:nil];
activityViewController.excludedActivityTypes = #[UIActivityTypePostToWeibo,UIActivityTypePrint,UIActivityTypeCopyToPasteboard,UIActivityTypeSaveToCameraRoll];
[activityViewController setValue:_dare.shtodo forKey:#"Did you see this?"];
[self presentViewController:activityViewController animated:YES completion:nil];
Thanks in advance for any suggestions on what I might be missing to get this to work.
You need to pass an instance or instances of your class that implements UIActivityItemSource when creating a UIActivityViewController with initWithActivityItems:applicationActivities: - see the documentation here. A view controller is probably not a good choice for this, I'd instead make dedicated objects to do this. Ultimately your code could look something like:
MySourceClass *mySource = [MySourceClass new];
NSArray *activityItems = #[mySource];
UIActivityViewController *activityViewController =
[[UIActivityViewController alloc] initWithActivityItems:activityItems
applicationActivities:nil];

How to share both NSAttritutedString and localfile(NSURL) using UIActivityItemProvider for UIActivityTypeMail?

I am written this code in MyActivityItemProvider,
[sharedObject objectForKey:#"Mail"] is NSArray containing NSAttributedString and NSURL for local file in mobile Storage,
I am unable to share both,
Please help any one
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
if([activityType isEqualToString:UIActivityTypeMail])
{
if ([sharedObject objectForKey:#"Mail"])
{
return [sharedObject objectForKey:#"Mail"][0];
}
}
}
You should be able to use the NSTextAttachment class to directly insert the file(s) you want to send into your NSAttributedString.
Edit
It looks like NSTextAttachments are ignored here.
While UIActivityItemProvider appears only able to represent a single sharable item (no returned arrays allowed), you can have multiple UIActivityItemProviders.
When you initialize your UIActivityViewController, simply pass it 2 different items. Here is an example where I've used a property on a single UIActivityItemProvider subclass to determine if it should return the body or an attachment.
MyItemSource *itemSourceBody = [[MyItemSource alloc] init];
itemSourceBody.returnAttachment = NO;
MyItemSource *itemSourceAttachment = [[MyItemSource alloc] init];
itemSourceBody.returnAttachment = YES;
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:#[itemSourceBody, itemSourceAttachment] applicationActivities:nil];
[self presentViewController:activityVC animated:YES completion:nil];
When returnAttachment is false, it returns the email body, when true it returns a URL to a locally stored PDF.
This is just an example, and not well architected.
Additionally, if you don't need a lot of customization, you could just initialize UIActivityViewController directly with your NSAttributedString and NSURL without the UIActivityItemProvider middleman.

How can I use an NSDictionary and map my array of strings to each UIViewController class

How can I use an NSDictionary and map my array of strings to each UIViewController class, and then use objectForKey and get the correct view controller for each key?
I am trying to have each item of my array push to its own view controller.
My array is:
NSArray *Cities:#["New York", "Chicago", "Miami"];
and i want the item #"New York" to push to NewYorkViewcontroller and #"Chicago" to chicagoviewvontroller etc.
You could consider having a different segue for each item, and in your dictionary store the segue identifier.
However, this will quickly get out of hand if you have more than a few items, in which case you are better off having a generic view controller with custom setup methods rather than doing it this way.
NSDictionary* mapping = #{"New York”: [NewYorkViewController class], … };
NSString* chosenCity;
Class vcClass = mapping[chosenCity];
UIViewController* vc = [[vcClass alloc] init];
Pseudo-code:
NSDictionary * myDictionary = [NSDictionary dictionaryWithObjects: newYorkViewController, chicagoviewcontroller forKeys: Cities[0], Cities[1]];
[self pushViewController: [myDictionary objectForKey:#"New York"] animated:YES];
you can use
#[#{#"title":#"New York",#"vc_class":[NewYorkViewController class]}, …];

iOS 6 - UIActivityViewController items [duplicate]

This question already has answers here:
Different data for sharing providers in UIActivityViewController
(4 answers)
Closed 9 years ago.
Hope everyone is aware of iOS 6 contains new style of ActionSheet (UIActivityViewController). The UIActivityViewController can be initiated with the paramentes like string, url, image etc. Below is the code snippet for that (where items is an array with string and url params).
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
But, is there any way that we can assign different parameters when we select different share options like Mail, Facebook or Twitter?
One method is we can implement UIActivityItemSource, where we need to implement the source methods
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
which always returns a string value. But I need to pass an Array, so that I can assign various parameters like URL, image and a title.
Any idea how we can achieve this?
You can not change anything for the built in iOS UIActivityViewController items like Mail, Facebook and Twitter. In order to implement custom actions for items in your UIActivityViewController you must create a custom subclass of UIActivity for each custom activity you want. Here is an example:
- (UIActivityViewController *)getActivityViewController {
MyFeedbackActivity *feedbackActivity = [[MyFeedbackActivity alloc] init];
MyFacebookActivity *facebookActivity = [[MyFacebookActivity alloc] init];
MyMailActivity *mailActivity = [[MyMailActivity alloc] init];
NSArray *applicationActivities = #[feedbackActivity, facebookActivity, mailActivity];
NSArray *activitiesItems = #[#"A string to be used for MyFeedbackActivity", #"A string to be used for MyFacebookActivity", #"A string to be used for MyMailActivity"];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activitiesItems applicationActivities:applicationActivities];
// Removed un-needed activities
activityVC.excludedActivityTypes = [[NSArray alloc] initWithObjects:
UIActivityTypeCopyToPasteboard,
UIActivityTypePostToWeibo,
UIActivityTypePostToFacebook,
UIActivityTypeSaveToCameraRoll,
UIActivityTypeCopyToPasteboard,
UIActivityTypeMail,
UIActivityTypeMessage,
UIActivityTypeAssignToContact,
nil];
return activityVC;
}
A very limited example of a subclassed UIActivity with documentation on the methods that you will be interested in overriding to handle your custom data/actions.
#import "MyFeedbackActivity.h"
#implementation MyFeedbackActivity
- (NSString *)activityType {
return #"MyFeedbackActivity";
}
- (NSString *)activityTitle {
return #"Feedback";
}
- (UIImage *)activityImage {
return [UIImage imageNamed:#"feedback"];
}
- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems {
return YES;
}
- (UIViewController *)activityViewController {
/**
* DESCRIPTION:
* Returns the view controller to present to the user.
* Subclasses that provide additional UI using a view controller can override this method to return that view controller. If this method returns a valid object, the system presents the returned view controller modally instead of calling the performActivity method.
* Your custom view controller should provide a view with your custom UI and should handle any user interactions inside those views. Upon completing the activity, do not dismiss the view controller yourself. Instead, call the activityDidFinish: method and let the system dismiss it for you.
*/
}
- (void)prepareWithActivityItems:(NSArray *)activityItems {
/**
* DESCRIPTION:
* Prepares your service to act on the specified data.
* The default implementation of this method does nothing. This method is called after the user has selected your service but before your service is asked to perform its action. Subclasses should override this method and use it to store a reference to the data items in the activityItems parameter. In addition, if the implementation of your service requires displaying additional UI to the user, you can use this method to prepare your view controller object and make it available from the activityViewController method.
*/
}
-(void)performActivity {
/**
* DESCRIPTION:
* Performs the service when no custom view controller is provided.
* The default implementation of this method does nothing. If your service does not provide any custom UI using the activityViewController method, override this method and use it to perform the activity. Your activity must operate on the data items received in the prepareWithActivityItems: method.
* This method is called on your app’s main thread. If your app can complete the activity quickly on the main thread, do so and call the activityDidFinish: method when it is done. If performing the activity might take some time, use this method to start the work in the background and then exit without calling activityDidFinish: from this method. Instead, call activityDidFinish: from your background thread after the actual work has been completed.
*/
}
#end

Provide different content to different services within a single UIActivityViewController

I'd like to provide different content to the different services in a UIActivityIndicatorView. For example, an HTML string for email, standard string for Facebook/Twitter, and an image for Copy. All of this happens within a single popover window.
I don't think this is possible. As I was playing around with all of the involved classes, I notices that the services that appear in the activity view change as the content type I provided it changed. In other words, when I provided a string, set A of services appeared. When I provided an image, set B of services appeared.
Next I tried adding both string and image thinking that possibly it would provide different content, however this just adds both the string and image to the services (for example an email is created with a string, then the image below).
Here is how I am providing string values (which are html links):
-(void)shareButtonTouchUpInside:(SMActionToolbarViewController*)sender{
// Reposition anchor view for UIPopoverController to point at
[self repositionAnchorViewToButtonFrame:self.actionToolbarViewController.shareButtonFrame];
// Asynch download of image
[SMUtility downloadAsset:self.selectedAsset completion:^(UIImage *image) {
// Create image source
SMActivitySource *activityImageSource = [[SMActivitySource alloc]initWithImage:image];
// Create string source
NSString *assetsString = [SMUtility assetsString:[NSArray arrayWithObject:self.selectedAsset]];
SMActivitySource *activityStringSource = [[SMActivitySource alloc]initWithString:assetsString];
// Present UIActiviyViewController within an UIPopoverController
NSArray *items = [#[activityImageSource, activityStringSource]mutableCopy];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc]initWithActivityItems:items applicationActivities:nil];
[activityViewController setCompletionHandler:^(NSString *activityType, BOOL completed){
// TODO: Populate mixpanel data
[SMMixPanel eventSharePhotoMethod:#"Unknown"];
}];
self.buttonPopoverController = [[UIPopoverController alloc] initWithContentViewController:activityViewController];
[self.buttonPopoverController presentPopoverFromRect:self.anchorView.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}];
}
You should subclass UIActivityItemProvider or create objects that conform to UIActivityItemSource. Pass in an array of those (one for each thing you want to share), and have some of them return nil depending on the chosen activity in the activityViewController:itemForActivityType: call.
This Question has been asked time and time again, the only way to fix it is to make your own UIActivity, see apple documentation on how to do that. Here is a link: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIActivity_Class/Reference/Reference.html

Resources