原文链接:http://nshipster.com/uiprintinteractioncontroller/
前言
UIKit使用户设备直接打印变得方便起来,而且打印可以根据内容或者纸张大小进行自定义排版。这篇文章的意义在于让你明白在打印的时候如何格式化你的内容,详细阐述不同的方式来展示打印接口。
Xcode6中,打印机模拟器作为硬件IO工具属于其的一部分。
UIKit Printing APIs
核心是UIPrintInteractionController
。这个类共享实例管理着打印任务还有展示给用户任何UI的配置。同样它还提供了3个级别对格式化内容的控制。
打印任务
首先看下打印选项配置
UIPrintInfo
UIPrintInfo
实例中存放着打印任务详情设置。你可以找到如下属性:
jobName String
:打印任务名字。它会在设备打印中心上展示,对于某些打印机会LCD展示哦。
orientation UIPrintInfoOrientation
:.Portrait
(默认)或是.Landscape
-通常这个属性被略去如果打印是固定朝向,比如PDF。duplex UIPrintInfoDuplex
:.None, .ShortEdge, or .LongEdge.
。长短边缘设置说明双面打印可被约束,但是None表示不支持双面打印。
outputType UIPrintInfoOutputType
:让UIKit知道你打印模式。可以为下面任意一种:
1).General (default)
:可图文混排,可双面打印
2).Grayscale
:如果内容只含黑体文本的话效果比General好
3).Photo
:彩色或者黑白图片,不允许双面打印,要求胶片纸质
4)PhotoGrayscale
:仅黑白效果的话比3)好
printerID String?
:特定打印机ID,通过这个检索到该打印机进行预设值。
另外,UIPrintInfo
提供了一个dictionaryRepresentation
属性,可以被保存并后续用来创建一个新的UIPrintInfo
实例。
UIPrintInteractionController设置
这里有些关于UIPrintInteractionController
的设置你需要配置下在开始展示打印UI之前。它们包括:
printInfo UIPrintInfo
:上述的配置
printPaper UIPrintPaper
:一个简单得类型来描述打印纸得物理性质和可打印的大小;除非是特别的应用,否则这将有UIKit代劳设置。showsNumberOfCopies Bool
:当设置为true的时候,用户可以设置拷贝的数量。showsPageRange Bool
:当设置为true,让用户在打印介质上选择打印范围。只有当有多页的内容时候这个选项才有意义,对于图片来说这个选项默认关闭。showsPaperSelectionForLoadedPapers Bool
:当设置为true并且选择的打印机有多打印纸的选项,那么UI会询问用户选择那个打印纸来打印。
将你的内容格式化
通过UIPrintInteractionController
四个不同的属性,你可以选择你想要的内容控制规格(复杂度)。
printingItem AnyObject! or printingItems [AnyObject]!
:最基础的水平,控制器只是简单得获取可打印的内容(图片或者是PDF)并将他们发送给打印器。
printFormatter UIPrintFormatter:
下一个等级,你可以使用UIPrintFormatter
子类在你的应用中格式化内容。你可以对格式进行一些控制,打印API基本完成剩下的部分。printPageRenderer UIPrintPageRenderer
:最高级水平,你可以创建UIPrintPageRenderer
子类,混合页面格式还有你自己所涉及的头、尾还有页面内容。
下面根据假想的感恩节菜谱来介绍这些打印属性。
根据printItem(s)
进行打印
你可以通过设置UIPrintInteractionController
的属性printItem
或printItems
来打印已存在且可以打印的内容。图片跟PDF可以以图片数据或者通过NSURL
地址引用加载到NSData
对象等形式提交。为了能够打印,图片必须是UIImage
支持的格式。
让我们来看这么一个简单的案例:展示UI当用户点击按钮的时候用来打印图片。处理流程基本相同,无论你要打印什么-配置你的打印信息,搭建打印交互控制器,并且在展示UI之前提供你的打印内容:
@IBAction func print(sender: UIBarButtonItem) {
if UIPrintInteractionController.canPrintURL(imageURL) {
let printInfo = UIPrintInfo(dictionary: nil)
printInfo.jobName = imageURL.lastPathComponent
printInfo.outputType = .Photo
let printController = UIPrintInteractionController.sharedPrintController()!
printController.printInfo = printInfo
printController.showsNumberOfCopies = false
printController.printingItem = imageURL
printController.presentAnimated(true, completionHandler: nil)
}
}
是吧,很容易吧。。。
这若是iPhone则使用
presentAnimated(:completionHandler:)
方法弹出打印UI。如果是iPad则使用presentFromBarButtonItem(:animated:completionHandler:) or presentFromRect(:inView:animated:completionHandler:)
。
UIPrintFormatter
UIPrintFormatter
类有两个子类(UISimpleTextPrintFormatter and UIMarkupTextPrintFormatter
)可以用来格式化文本附加上另外一个UIViewPrintFormatter
可以格式化受支持的三种视图内容:UITextView, UIWebView, 和 MKMapView
。打印格式有一些属性允许你去用不同的方式定义页面打印区域;
contentInsets UIEdgeInsets
:整体内容在打印纸四个边缘内偏移一套值。左右适用用每个页面,但是上部偏移只适用于首页。底部偏移忽略。
perPageContentInsets UIEdgeInsets
(仅限iOS8):每一页格式化后的内容四个边缘内部偏移一套值。maximumContentWidth and maximumContentHeight CGFloat
:如果有指定,可以进一步约束内容区域的长与高。尽管苹果没有文档化说明,所有这些值还是基于每英寸72个点。
两文本类打印格式化与文本一起初始化,文本将会被格式。UISimpleTextPrintFormatter
将会处理一半或者属性文本,而UIMarkupTextPrintFormatter
使用markupText
属性处理和渲染HTML文本。让我们试着发送一个HTML版本的Swiss chard菜谱通过装饰格式化:
let formatter = UIMarkupTextPrintFormatter(markupText: htmlString)
formatter.contentInsets = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72) // 1" margins
printController.printFormatter = formatter
结果如下,漂亮的经过渲染过的HTML页面:
另外一方面,使用UIViewPrintFormatter
,你可以通过它的viewPrintFormatter
属性检索到你想要打印的字段:这里展示下这个格式化在三种支持的视图下的时候怎么完成这个任务:
1) UITextView
2) UIWebView
3) MKMapView
UIPrintPageRenderer
内置的格式化好用,但是大部分对打印纸的控制,你可以通过继承UIPrintPageRenderer
来执行。在子类中你可以混合上述打印的格式化使用你自定义的绘制方法为你的应用内容创建很牛X的排版。让我们看下更多打印菜谱的方式,此时使用的是在菜谱中添加了一个头以及在一段文字旁边画了一张图片的页面渲染。
在初始化时,我们保存了我们将要打印的数据,然后设置headerHeight
并为菜谱文本创建了装饰文本格式化。
戳这里点击示例查阅
class RecipePrintPageRenderer: UIPrintPageRenderer {
let authorName: String
let recipe: Recipe
init(authorName: String, recipe: Recipe) {
self.authorName = authorName
self.recipe = recipe
super.init()
self.headerHeight = 0.5 * POINTS_PER_INCH
self.footerHeight = 0.0 // default
let formatter = UIMarkupTextPrintFormatter(markupText: recipe.html)
formatter.perPageContentInsets = UIEdgeInsets(top: POINTS_PER_INCH, left: POINTS_PER_INCH,
bottom: POINTS_PER_INCH, right: POINTS_PER_INCH * 3.5)
addPrintFormatter(formatter, startingAtPageAtIndex: 0)
}
// ...
}
当你使用一种或者多种打印格式化作为你自定义渲染器的一部分的时候,UIKit查询要进行如此渲染的也页面数。如果你确定要自定义的页面排版,实现
umberOfPages()
来提供一个正确值。
接下来,我们需要重写drawHeaderForPageAtIndex(:inRect:)
来画出我们的自定义头。不幸的是原来每个页面打印格式化中简单内容的内偏值现在不在了,所以我们首先需要让headerRect
参数来适应我的边沿,然后简单地划入当前图形上下文。这里有一个简单的drawFooterForPageAtIndex(:inRect:)
方法来画足部。
override func drawHeaderForPageAtIndex(pageIndex: Int, var inRect headerRect: CGRect) {
var headerInsets = UIEdgeInsets(top: CGRectGetMinY(headerRect), left: POINTS_PER_INCH, bottom: CGRectGetMaxY(paperRect) - CGRectGetMaxY(headerRect), right: POINTS_PER_INCH)
headerRect = UIEdgeInsetsInsetRect(paperRect, headerInsets)
// author name on left
authorName.drawAtPointInRect(headerRect, withAttributes: nameAttributes, andAlignment: .LeftCenter)
// page number on right
let pageNumberString: NSString = "\(pageIndex + 1)"
pageNumberString.drawAtPointInRect(headerRect, withAttributes: pageNumberAttributes, andAlignment: .RightCenter)
}
最后,让我们实现drawContentForPageAtIndex(:inRect:)
:
override func drawContentForPageAtIndex(pageIndex: Int, inRect contentRect: CGRect) {
if pageIndex == 0 {
// only use rightmost two inches of contentRect
let imagesRectWidth = POINTS_PER_INCH * 2
let imagesRectHeight = paperRect.height - POINTS_PER_INCH - (CGRectGetMaxY(paperRect) - CGRectGetMaxY(contentRect))
let imagesRect = CGRect(x: CGRectGetMaxX(paperRect) - imagesRectWidth - POINTS_PER_INCH, y: paperRect.origin.y + POINTS_PER_INCH, width: imagesRectWidth, height: imagesRectHeight)
drawImages(recipe.images, inRect: imagesRect)
}
}
当我们自定义页面渲染器实现完成,我们可以设置一个实例作为打印交互控制器的pageRenderer
属性,这样我们就准备好打印工作了。
let renderer = RecipePrintPageRenderer(authorName: "Nate Cook", recipe: selectedRecipe)
printController.printPageRenderer = renderer
最后的结果自然比内置格式化器炫多了。
值得注意的是菜单文字是有
UIMarkupTextPrintFormatter
进行格式化的,而头部还有图片是通过自定义代码格式化的。
打印共享页面
使用上述描述的工具,在共享页面添加打印能力变得简单起来。与使用UIPrintInteractionController
弹出打印UI不同,我们传递我们自己配置UIPrintInfo
以及打印项,格式化,或者是渲染器给UIActivityViewController
。如果用户在共享页面选择打印按钮,打印UI会完整展示我们的设置。
@IBAction func openShareSheet() {
let printInfo = ...
let formatter = ...
let activityItems = [printInfo, formatter, textView.attributedText]
let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
presentViewController(activityController, animated: true, completion: nil)
}
当
UIPrintInfo
和UIPrintFormatter
以及UIPrintPageRenderer
的子类可以以活动的形式传递给UIActivityViewController
的时候,它们似乎都不遵循UIActivityItemSource
协议,所以你会看到一个告警"Unknown activity items."
跳过打印UI
现在iOS8可以略过打印UI。你可以在应用中提供一个方式让用户选中一个打印机进行更方便的使用UIPrinterPickerController
。它在构造器中接受UIPrinter
实例选项来进行预选择,使用上述同样的弹出选项,并且在用户选择她得打印器的时候有一个响应句柄:
let printerPicker = UIPrinterPickerController(initiallySelectedPrinter: savedPrinter)
printerPicker.presentAnimated(true) {
(printerPicker, userDidSelect, error) in
if userDidSelect {
self.savedPrinter = printerPicker.selectedPrinter
}
}
现在你可以让你的UIPrintInteractionController
通过调用printToPrinter(:completionHandler:)
通过保存过的打印机进行直接打印而不是通过调用present...
方法。
最后一个建议。。。略。。。呵呵
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。