AirPrint - iOS Components and Frameworks: Understanding the Advanced Features of the iOS SDK (2014)

iOS Components and Frameworks: Understanding the Advanced Features of the iOS SDK (2014)

Chapter 11. AirPrint

Without a doubt, printing is going the way of the Dodo Bird and the Tasmanian Tiger, but it will not happen overnight. There are plenty of solid autumn years left for physical printing. With iOS 4.2, Apple introduced a new SDK feature called AirPrint, which as the name implies allows an iOS device to print to a wireless printer.

AirPrint Printers

There is a limited selection of AirPrint-enabled printers, even three years after the release of AirPrint. Most major printer manufacturers now have at least a few models that support AirPrint. Apple also maintains a list of compatible printers through a tech note (http://support.apple.com/kb/HT4356). In addition, some third-party Mac applications enable AirPrint for any existing printer, such as Printopia ($19.95 at www.ecamm.com/mac/printopia).

Since the release of AirPrint, there have been numerous blogs, articles, and how-tos written on testing AirPrint. Apple seems to have taken notice of the need and has started to bundle an app with the developer tools called Printer Simulator (Developer/Platforms/iPhoneOS.platform/Developer/Applications), as shown in Figure 11.1. Printer Simulator allows your Mac to host several printer configurations for the iOS Simulator to print to; using this tool, you can test a wide range of compatibility.

Image

Figure 11.1 The Printer Simulator tool.

The sample app for this chapter is a simple iPhone app that provides the user with two views, as shown in Figure 11.2. The first view is a simple text editor, which will demo how to lay out pages and text. The second view is a Web browser, which will demo how to print rendered HTML as well as PDF screen grabs.

Image

Figure 11.2 Print, the sample app for AirPrinting.

The Print app is simple and does not contain a significant amount of overhead code. It is based on the Apple TabBarController default project, containing two tabs. The text editor contains a UITextView as well as two buttons: one to hide the keyboard, the other to begin the print process. The Web browser view contains a Print button but also needs a URL entry text field.

Testing for AirPrint

It is important to test for AirPrint support in your software before enabling the functionality to users, because some of your users might be running an iOS version that does not support AirPrint. In the sample app this is performed as part of theapplication:didFinishLaunchingWithOptions: method using the following code snippet:

if (![UIPrintInteractionController isPrintingAvailable])
{
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"Error"
message:@"This device does not support printing!"
delegate:nil
cancelButtonTitle:@"Dismiss"
otherButtonTitles:nil];

[alert show];
[alert release];
}


Note

AirPrint is defined as part of UIKit so there are no additional headers or frameworks that need to be imported.


Printing Text

Printing text is probably the most common use case for not just AirPrint but any kind of print job. Using AirPrint to print text can be complex if you aren’t familiar with printing terminology. The following code is from the ICFFirstViewController.m class of the sample app. Look at it as a whole first; later in this section, it will be broken down by each line and discussed.

- (IBAction)print:(id)sender
{
UIPrintInteractionController *print = [UIPrintInteractionController sharedPrintController];

print.delegate = self;

UIPrintInfo *printInfo = [UIPrintInfo printInfo];
printInfo.outputType = UIPrintInfoOutputGeneral;
printInfo.jobName = @"Print for iOS";
printInfo.duplex = UIPrintInfoDuplexLongEdge;
print.printInfo = printInfo;

print.showsPageRange = YES;

UISimpleTextPrintFormatter *textFormatter = [[UISimpleTextPrintFormatter alloc] initWithText:[theTextView text]];

textFormatter.startPage = 0;

textFormatter.contentInsets = UIEdgeInsetsMake(36.0, 36.0,36.0, 36.0);

textFormatter.maximumContentWidth = 540;

print.printFormatter = textFormatter
}

The preceding code takes the contents of a text view and prints it formatted for 8 1/2- by 11-inch paper with 1/2-inch margins on all sides. The first thing you need to do whenever you want to prepare for a print operation is create a reference to the sharedPrintController using the following code snippet:

UIPrintInteractionController *print = [UIPrintInteractionController sharedPrintController];

In the next line of the sample app, a delegate is set. The delegate methods for printing are defined in the upcoming section “UIPrintInteractionControllerDelegate.”

Print Info

The next step is to configure the print info. This specifies a number of controls and modifiers for how the print job is set up. You can obtain a new default UIPrintInfo object using the UIPrintInfo singleton.

The first property that is set on the print info in the sample app is outputType. In the example, the output is set as UIPrintInfoOutputGeneral. This specifies that the print job can be a mix of text, graphics, and images, as well as setting the default paper size to letter. Other options for outputType include UIPrintInfoOutputPhoto and UIPrintInfoOutputGrayscale.

The next property that is set is the jobName. This is an optional field that is used to identify the print job in the print center app. If you do not set a jobName, it is defaulted to the app’s name.

Duplexing in the printer world refers to how the printer handles double-sided printing. If the printer does not support double-sided printing, these properties are ignored. You can supply UIPrintInfoDuplexNone if you would like to prevent double-sided printing. To use double-sided printing you have two options: UIPrintInfoDuplexLongEdge, which will flip the back page along the long edge of the paper, and UIPrintInfoDuplexShortEdge, which, as the name implies, flips the back page along the short side of the paper.

In addition to the printInfo properties that are used in the sample code, there are two additional properties. The first, orientation, allows you to specify printing in either landscape or portrait. The second is printerID, which allows you to specify a hint on which printer to use.PrinterID is often used to automatically select the last used printer, which can be obtained using the UIPrintInteractionControllerDelegate.

After you configure the printInfo for the print job, you need to set the associated property on the UIPrintInteractionController. An example is shown in the next code snippet:

print.printInfo = printInfo;

Setting Page Range

In many cases, a print job will consist of multiple pages, and occasionally you will want to provide the user with the option of selecting which of those pages is printed. You can do this through the showsPageRange property. When set to YES, it will allow the user to select pages during the printer selection stage.

print.showsPageRange = YES;

UISimpleTextPrintFormatter

After configuring the printInfo, the UIPrintInteractionController has a good idea of what type of print job is coming but doesn’t yet have any information on what to print. This data is set using a print formatter; this section discusses the print formatter for text. In following sections, additional print formatters are discussed in depth.

UISimpleTextPrintFormatter *textFormatter = [[UISimpleTextPrintFormatter alloc] initWithText:[theTextView text]];

textFormatter.startPage = 0;

textFormatter.contentInsets = UIEdgeInsetsMake(36.0, 36.0, 36.0, 36.0);

textFormatter.maximumContentWidth = 540;

print.printFormatter = textFormatter;
[textFormatter release];

When you create a new instance of the UISimpleTextPrintFormatter, it is allocated and initialized with the text you will be printing. The sample app will print any text that appears in the UITextView.

The first property that is set in the sample app is for the startPage. This is a zero-based index of the first page to be printed. The sample app will begin printing from page one (index 0).

On the following line contentInserts are set. A value of 72.0 equals one inch on printed paper. The sample app will be providing half-inch values on all sides; this will print the text with half-inch margins on the top, bottom, left, and right. Additionally, the maximum width is set to 504, which specifies a 7 1/2-inch printing width (72.0×7.0).

There are two additional properties that were not used in the sample app. The font property allows you to specify a UIFont that the text is to be printed in. font is an optional property; if you do not specify a font, the system font at 12-point is used. You can also specify a text color using the color property. If you do not provide a color, [UIColor blackColor] is used.

When you finish configuring the textFormatter, you will need to set it to the printFormatter property of the UIPrintInteractionController object.

Error Handling

It is always important to gracefully handle errors, even more so while printing. With printing, there are any number of things that can go wrong outside of the developer’s control, from out-of -paper issues to the printer not even being on.

The sample app defines a new block called completionHandler. This is used to handle any errors that are returned from the print job. In the next section, you will begin a new print job with the completionHandler block as one of the arguments.

void (^completionHandler)(UIPrintInteractionController *,BOOL, NSError *) = ^(UIPrintInteractionController *print,BOOL completed, NSError *error)
{
if (!completed && error)
{
NSLog(@"Error!");
}
};

Starting the Print Job

After you have created a new UIPrintInteractionController, specified the printInfo and the printFormatter, and created a block to handle any errors that are returned, you can finally print something. Call the method presentAnimated:completionHandler: on the UIPrintInteractionController object using the completion block that was created in the preceding section. This will present the user with the Printer Options view, as shown in Figure 11.3.

[print presentAnimated:YES completionHandler:completionHandler];

Image

Figure 11.3 Print options while printing text with multiple pages on a printer that supports double-sided printing.

Depending on the selected printer and the amount of text being printed, the options will vary. For example, if the print job is only one page, the user will not be presented with a range option; likewise, if the printer does not support double-sided printing, this option will be disabled.

Print Simulator Feedback

If you printed to the Printer Simulator app as discussed in the “AirPrint Printers” section, after the print job is finished, a new page will be opened in preview (or your default PDF viewing application) showing how the final page will look. An example using the print info and print formatter information from this section is shown in Figure 11.4.

Image

Figure 11.4 A print preview shown when using the Printer Simulator; notice the highlighted margins.

Print Center

Just as on desktop computers, iOS provides users a way to interact with the current print queue. While any print job is active on an iOS device, a new app appears in the active app area. The Print Center app icon is shown in the Springboard in Figure 11.5. The Print Center app itself is shown in Figure 11.6. The Print Center was removed in iOS 7 and printing feedback is now handled during the print process.

Image

Figure 11.5 While printing is occurring, a new app will appear on an iOS 6.0 device called Print Center.app. Print Center.app is badged with the current number of print jobs. This behavior no longer exists on iOS 7.

Image

Figure 11.6 Print Center.app provides information about the current print job as well as a way to cancel printing.

UIPrintInteractionControllerDelegate

As shown earlier in the “Printing Text” section, you can optionally provide a delegate for a UIPrintInteractionController object. The possible delegate callbacks are used in both views of the sample app. Table 11.1 describes these delegate methods.

Image

Table 11.1 Listing of the Available UIPrintInteractionControllerDelegate Methods

Printing Rendered HTML

Printing rendered HTML is handled automatically through a print formatter in an almost identical manner as printing plain text. The following method handles printing an HTML string, which is retrieved from the UIWebView in the sample app. You might notice that this is similar to how text was printed in the previous example. Take a look at the method as a whole; it is discussed in detail later in this section.

- (IBAction)print:(id)sender
{
UIPrintInteractionController *print = [UIPrintInteractionController sharedPrintController];

print.delegate = self;

UIPrintInfo *printInfo = [UIPrintInfo printInfo];
printInfo.outputType = UIPrintInfoOutputGeneral;
printInfo.jobName = @"Print for iOS";
printInfo.duplex = UIPrintInfoDuplexLongEdge;
print.printInfo = printInfo;

print.showsPageRange = YES;

NSURL *requestURL = [[theWebView request] URL];
NSError *error;

NSString *contentHTML = [NSString
stringWithContentsOfURL:requestURL
encoding:NSASCIIStringEncoding
error:&error];

UIMarkupTextPrintFormatter *textFormatter = [[UIMarkupTextPrintFormatter alloc] initWithMarkupText:contentHTML];

textFormatter.startPage = 0;

textFormatter.contentInsets = UIEdgeInsetsMake(36.0, 36.0, 36.0, 36.0);

textFormatter.maximumContentWidth = 540;
print.printFormatter = textFormatter;
[textFormatter release];

void (^completionHandler)(UIPrintInteractionController *,BOOL, NSError *) = ^(UIPrintInteractionController *print,BOOL completed, NSError *error)
{
if (!completed && error)
{
NSLog(@"Error!");
}
};

[print presentAnimated:YES completionHandler:completionHandler];
}

The first thing that needs to be done as it was in the text printing is to create a new reference to a UIPrintInteractionController. The next step is to set the printInfo for the upcoming print job. Nothing in this code block dealing with printing differs from the printing text example; refer to that section for details on these properties.

Printing PDFs

AirPrint has built-in support for printing PDF files. PDF is arguably the easiest type of file to print when you have the PDF data. Before a PDF file can be printed, first the UIPrintInteractionController and associated UIPrintInfo need to be set up. This setup is done exactly the same as in the previous example in the section “Printing Rendered HTML.” In the sample app, the PDF is generated from the UIWebView from the preceding section; however, you can specify any source for the PDF data. After the data has been created using the renderInContextmethod, you can assign that image value to printingItem. This method can also be used to print any UIImage data.


Note

The sample app does not currently have an action hooked up to the print PDF method. You will have to assign a button to that method in order to use it.


- (IBAction)printPDF:(id)sender
{
UIPrintInteractionController *print =
[UIPrintInteractionController sharedPrintController];

print.delegate = self;
UIPrintInfo *printInfo = [UIPrintInfo printInfo];
printInfo.outputType = UIPrintInfoOutputGeneral;
printInfo.jobName = @"Print for iOS";
printInfo.duplex = UIPrintInfoDuplexLongEdge;
print.printInfo = printInfo;

print.showsPageRange = YES;

UIGraphicsBeginImageContext(theWebView.bounds.size);

[theWebView.layer
renderInContext:UIGraphicsGetCurrentContext()];

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

print.printingItem = image;

void (^completionHandler)(UIPrintInteractionController *,BOOL, NSError *) = ^(UIPrintInteractionController *print,BOOL completed,NSError *error)
{
if (!completed && error)
{
NSLog(@"Error!");
}
};

[print presentAnimated:YES completionHandler:completionHandler];
}

Summary

In this chapter, you learned how to print documents, images, and HTML from an iOS device using AirPrint. You should have a firm grasp of the knowledge required to create new print jobs, provide the materials to be printed, format the output, handle errors, and interact with various printers. The sample app provided for this chapter walked you through the process of printing plain text, HTML, and PDF data. You should feel confident adding AirPrint support into any of your existing or future iOS projects.

Exercises

1. Set up a print operation that prints assets from the photo library. Refer to Chapter 23, “Accessing Photo Libraries,” for information on how to retrieve the photo objects. Be sure to update the output type to get better photo quality.

2. Update the app to print the contents of the device screen so that you can quickly take snapshots of your app and have them printed.