I am trying to implement a custom activity to the standard activities (Print, Mail, FaceBook, etc.) but for now only want the standard Print (for AirPrint) and my own custom printing by a direct method. I am obviously missing something fundamental as none of the methods in my custom class ever get called. For now I only have some NSLog statement to figure out the calling sequence, and to get the framework to function.
The following is my test code for the custom activity class:
// PrintActivity.h
#import <UIKit/UIKit.h>
@interface PrintActivity : UIActivity
@end
And the .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 invocation in the main routine:
- (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 custom class gets called, but the system Mail, Message, and Copy icons show in the activity sheet and not the Print icon. I expected just the system Print icon (and my own).
If I uncomment the top block of statements (and comment out the NSArray *activityItems ...........) further down, the systme Mail, Message, Print, and Copy icons. In this experiment I think I am mixing different methods by creating my own formatter, but that seemed to be the suggestion at WWDC 2012 ?
If I then uncomment the line with 'excludeActivityTypes' I only get the system Print icon.
I would welcome any input to help me figuring this out.
And if anybody know of any example code to do what I want, that would be terrific.
Edit: Updated code to my working code.
I was pulling my hair out over UIActivity as well this past tweek, it really needs to be explained better by Apple and have more features added; Try this:
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's 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
Don't forget to make these two files as well:
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 it:
- (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];
}
@troop231 - great answer, and very helpful.
The only thing I'd add is to be sure to signal completion of the operation or the completion routine won't get called and the popover won't dismiss. Something like:
- (void)performActivity {
NSLog(@"Do the actual activity here");
// My custom code here
[self activityDidFinish:YES]; // indicate completion here!!
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With