As I wrote recently, the ability to create a PDF document from an application’s printed content is one of my favorite features that Apple added in iOS 9. This feature leverages the printing system so that if your application can print, a user will also be able to use the new “Save PDF to iBooks” feature. Well, almost.
The key is that your application needs to offer printing by using Apple’s “Share Sheet”, instead of using the older method of using a UIPrintInteractionController. And unfortunately, as of my writing this, all of Apple’s sample code demonstrating how to implement printing in an iOS application uses the UIPrintInteractionController method of presenting printing to the user.
I ran into this recently when I emailed the developers of one of my favorite iOS applications and suggested they add support for the “Save PDF to iBooks” to their app. After all, the app already supported the Share Sheet for a number of tasks other than printing but it supported printing using the old UIPrintInteractionController. The developer was kind enough to reply to my suggestion with interest, but the response I got was: “I’d like to but I don’t know how to do that.”
Poking around Apple’s developer site, I found the handy page AirPrint for Developers. It includes a link to the 2014 WWDC session on AirPrint (there was none in 2015) which is a great presentation with tons of info for app developers getting started with printing on iOS. And the AirPrint for Developers page has four links to sample code that implement printing in various ways. Unfortunately none of these samples implement the Share Sheet method of printing.
So I grabbed one of Apple’s samples and updated it. I started with one that I myself wrote (long ago) at Apple. It demonstrates how to print content from a WebView in both a basic way and also by using some fancier techniques to add more control to the layout of content on the printed pages. Updating it to use the Share Sheet is pretty straightforward.
The only substantive change needed to make the sample app use the share sheet is to take out the use of the UIPrintInteractionController from the sample and replaced it with the use of a UIActivityViewController. This controller takes an array of “Activity Items” as part of its initialization. When the controller is presented it determines what activities (or actions) can be taken on those activity items and allows the user to choose from those actions.
The UIActivityViewController determines that printing is a supported activity if the activity items array contains both an object of the UIPrintInfo class and also a source of print data. The source of print data can be a printing item or items (e.g. a photo or photos), a UIPrintFormatter object, or a UIPrintPageRenderer subclass. For the original sample code, the source of print data was a UIPrintPageRenderer subclass that the code created and assigned to the myRenderer local variable. For fun in the new code I also added an NSURL corresponding to the URL of the web page the user is visiting. This allows the Share Sheet to show more actions for the sample app than just those associated with printing. Once the UIActivityViewController is created, all that is needed is to present it and UIKit does the rest. I mostly stole the code to present the activity view controller from this sample.
Here’s the updated code snippet that replaces the old usage of the UIActivityViewController in the original sample:
NSURL *url = [NSURL URLWithString:[self.urlField text]]; NSArray *activityItemsArray = [NSArray arrayWithObjects: myRenderer, printInfo, url, nil]; UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItemsArray applicationActivities:nil]; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { //iPad, present the view controller inside a popover if(!self.activityPopover){ self.activityPopover = [[UIPopoverController alloc] initWithContentViewController:activityViewController]; } if (![self.activityPopover isPopoverVisible]) { [self.activityPopover presentPopoverFromBarButtonItem:self.printButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; }else{ //Dismiss if the button is tapped while pop over is visible [self.activityPopover dismissPopoverAnimated:YES]; } }else{ //iPhone, present activity view controller as is [self presentViewController:activityViewController animated:NO completion:nil]; }
Running the modified app on an iPad and touching the action icon brings up the Share Sheet instead of the Printing UI directly (as it would have if I’d not modified the original sample). From there you can see that both the Print icon as well as the “Save PDF to iBooks” icon appears on the Share Sheet. And both behave as expected!
If you’ve already got printing working in your app, now’s the time to update to using the Share Sheet method instead of the older method. Without a lot of extra work on your part, users get a great new option to save your application’s content in a print ready format. And if you’ve not added printing to your application to date, the “Save PDF to iBooks” feature provides yet another reason to do so.