Issues with adding user activity to UIActivityController

I'm trying to implement the usual activities for standard actions (Print, Mail, FaceBook, etc.), but at the moment you only need the standard Print (for AirPrint) and my own direct printing method. I obviously missed something fundamental, since none of the methods of my user class were ever called. At the moment, I only have an NSLog instruction to figure out the sequence of calls and make the frame function.

Below is my test code for a custom activity class:

// PrintActivity.h #import <UIKit/UIKit.h> @interface PrintActivity : UIActivity @end 

And .m

 #import "PrintActivity.h" @interface PrintActivity () @property (nonatomic, strong) UIWebView *dummyWebView; @end @implementation PrintActivity - (NSString *)activityType { NSLog(@"activityType"); return @"MetriScan Print"; } - (NSString *)activityTitle { NSLog(@"activityTitle"); return @"MetriScan\nPrint"; } - (UIImage *)activityImage { NSLog(@"activityImage"); UIImage *icon = [UIImage imageNamed:@"metriscan_57_c2a_3.png"]; return icon; } - (BOOL)canPerformWithActivityItems:(NSArray *)activityItems { NSLog(@"canPerformWithActivityItems"); return YES; } - (void)prepareWithActivityItems:(NSArray *)activityItems { NSLog(@"prepareWithActivityItems"); } - (void)performActivity { NSLog(@"Do the actual printing here"); // My custom code here } 

And this is the call in the main procedure:

 - (IBAction)printReport:(UIBarButtonItem *)sender { NSLog(@"Print Report"); PrintActivity *metriscanPrint = [[PrintActivity alloc] init]; UIViewPrintFormatter *printFormatter = [self.webView viewPrintFormatter]; NSArray *activityItems = [NSArray arrayWithObjects:printFormatter, nil]; NSArray *appActivities = [NSArray arrayWithObjects:metriscanPrint, nil]; UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:appActivities]; //activityController.excludedActivityTypes = [NSArray arrayWithObjects:UIActivityTypePostToFacebook, UIActivityTypePostToTwitter, UIActivityTypePostToWeibo, UIActivityTypeMail, UIActivityTypeMessage, nil]; activityController.completionHandler = ^(NSString *activityType, BOOL completed) { sender.enabled = YES; }; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { [self presentViewController:activityController animated:YES completion:nil]; } else { sender.enabled = NO; self.printPop = [[UIPopoverController alloc] initWithContentViewController:activityController]; [self.printPop presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES]; } 

As I said, none of the methods in the user class are called, but the system mail, message, and copy icons appear on the activity sheet, not on the Print icon. I expected that there would only be a "Print System" icon (and my own).

If I uncomment the top block of statements (and comment out NSArayArray * activityItems ...........) further down, the Mail, Message, Print and Copy systme icons. In this experiment, I think I'm mixing different methods, creating my own formatter, but this seemed to be a proposal at WWDC 2012?

If I uncomment the line with "excludeActivityTypes", I get only the "Print System" icon.

I would like to welcome any materials that will help me understand this.

And if anyone knows any sample code to do what I want, that would be awesome.

Edit: Updated code for my working code.

+8
android-activity ios uiactivity
source share
3 answers

I pulled my hair for UIActivity, as well as for last tweek, it really needs to be better explained to Apple and add more features; Try the following:

PrintActivity.h

 #import <UIKit/UIKit.h> @interface PrintActivity : UIActivity @end 

PrintActivity.m

 #import "PrintActivity.h" @implementation PrintActivity - (NSString *)activityType { return @"MetriScan.Print"; } - (NSString *)activityTitle { return @"Print MtriScan"; } - (UIImage *)activityImage { //***** Note: I recommend using two sizes, as the iPad UIActivity image size differs from //***** the iPhone's. Also, create @2x sizes for Retina compatible devices. So you will //***** have a total of 4 images. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { return [UIImage imageNamed:@"test_72.png"]; } return [UIImage imageNamed:@"test_57.png"]; } - (BOOL)canPerformWithActivityItems:(NSArray *)activityItems { NSLog(@"%s", __FUNCTION__); return YES; } - (void)prepareWithActivityItems:(NSArray *)activityItems { NSLog(@"%s",__FUNCTION__); } - (UIViewController *)activityViewController { NSLog(@"%s",__FUNCTION__); return nil; } - (void)performActivity { // This is where your custom print code should go } @end 

Remember to also make these two files:

PrintProvider.h

 #import <UIKit/UIKit.h> @interface PrintProvider : UIActivityItemProvider <UIActivityItemSource> @end 

PrintProvider.m

 #import "PrintProvider.h" @implementation PrintProvider #pragma mark - UIActivityItemSource - (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType { NSLog(@"%s",__FUNCTION__); NSLog(@"%@", activityType); return [super activityViewController:activityViewController itemForActivityType:activityType]; } @end 

Now we can finally call him:

 - (IBAction)printReport:(UIBarButtonItem *)sender { CustomProvider *customProvider = [[CustomProvider alloc]init]; NSArray *items = [NSArray arrayWithObjects:customProvider,nil]; CustomActivity *ca = [[CustomActivity alloc]init]; UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:[NSArray arrayWithObject:ca]]; activityVC.excludedActivityTypes = @[UIActivityTypePostToWeibo, UIActivityTypeAssignToContact,UIActivityTypeCopyToPasteboard, UIActivityTypeSaveToCameraRoll,UIActivityTypeMail,UIActivityTypePostToTwitter, UIActivityTypePostToFacebook,UIActivityTypeMessage]; activityVC.completionHandler = ^(NSString *activityType, BOOL completed) { NSLog(@" activityType: %@", activityType); NSLog(@" completed: %i", completed); }; self.popoverController = [[UIPopoverController alloc] initWithContentViewController:activityVC]; CGRect rect = [[UIScreen mainScreen] bounds]; [self.popoverController presentPopoverFromRect:rect inView:self.view permittedArrowDirections:0 animated:YES]; } 
+7
source share

@ troop231 is a great answer, and very helpful.

The only thing I would like to add is to make sure that the signal is complete or the completion operation will not be called and the popover will not be canceled. Something like:

 - (void)performActivity { NSLog(@"Do the actual activity here"); // My custom code here [self activityDidFinish:YES]; // indicate completion here!! } 
+3
source share

thanks for your help guys! And, for the activity image, it seems we need to provide the image following these Apple docs:

The alpha channel of the image is used as a mask to generate the final image that is presented to the user. Any color data in the image itself is ignored. Opaque pixels have a gradient towards them and this gradient is then laid on top of the standard background. Thus, a fully opaque image will give a gradient filled rectangle. For iPhone and iPod touch, images should not exceed 43 by 43 points (which is equivalent to 86 by 86 pixels for devices with Retina displays.) For iPad images should be no more than 55 x 55 pixels (which corresponds to 110 by 110 pixels for iPad with displays Retina.)

-one
source share

All Articles