PDFKit框架详细解析 (二) —— 基于PDFKit的PDF文档的创建和分享(一)

版本记录

版本号 时间
V1.0 2019.09.16 星期一

前言

今天翻阅苹果的API文档,发现多了一个框架就PDFKit,看了下才看见是iOS11.0新添加的框架,这里我们就一起来看一下框架PDFKit。感兴趣的可以看下面几篇文章。
1. PDFKit框架详细解析 (一) —— 基本概览(一)

开始

今天过生日,祝自己生日快乐吧!

首先看下主要内容

主要内容:了解如何创建PDF,使用Core TextCore Graphics并通过构建应用程序来共享创建的文档。

接着看一下写作环境

Swift 5, iOS 13, Xcode 11

PDF文档充当在线和打印文档之间的桥梁。 Adobe发明了PDF格式,但它现在是一个开放标准,它定义了包含文本,图像和交互元素的文档。

此标准允许您创建看起来像打印文档的电子文档。 对于许多应用程序,创建PDF可为用户增加价值。

PDFKit在版本11.0中来到iOS。 它提供了用于显示,创建和操作PDF文档的库。

虽然PDFKit最常见的用途是添加查看PDF文档到应用程序的功能,但您也可以使用它来创建和更改PDF文件。

在本教程中,您将构建一个应用程序,根据用户输入的信息创建一个简单的flyer作为PDF。

在继续之前,您应该对iOS应用程序开发有基本的了解。 熟悉Core Text and Core Graphics将很有用,但不是必需的。

PDF文档由以.pdf扩展名结尾的单个文件组成。 该文件可以包括提供关于文档的信息的元数据。 该规范还允许简单的密码保护来限制访问和打印。

PDF文档包含与打印文档页面对应的页面。 每个页面包含文本,图像,超链接和/或注释。

该页面使用一个坐标系统,每个打印英寸有72个点。 坐标起源于页面的左下角,并向上和向右增加。

在本教程中,您将构建一个应用程序来创建简单的传单flyer。 传单将包含标题,图像,正文和底部的几个撕下标签。 该应用程序允许用户输入传单的信息,并从照片库或相机中选择照片。 用户选择图像后,将显示该图像的预览。 这是传单的外观:

打开入门项目,然后构建并运行应用程序。

您将看到一个表单,用于输入创建传单的信息。 工具栏包含一个预览Preview按钮,该按钮将创建PDF并打开带有PDFView的视图控制器以显示传单。 还有一个Share按钮,您将更新,以便您可以与其他iOS应用程序共享PDF。


Creating a PDF With PDFKit

创建PDF的关键类是UIGraphicsPDFRenderer。 过程是:

  • 1) 实例化UIGraphicsPDFRenderer对象,可选择提供描述文档的参数。 您通常希望这样做,因为如果不这样做,iOS将使用基于用户设备的默认值。
  • 2) 调用pdfData(actions :)渲染器生成Data对象,或调用writePDF(to:withActions :)渲染器将文档直接写入磁盘。
  • 3) 在渲染器方法的闭包中使用Core Graphics指令来创建文档。

接下来,您将创建一个新类来封装构建PDF的过程。 在项目中选择File ▸ New ▸ File…,然后选择iOS▸CocoaTouch Class模板。 然后单击Next

完成后,将类命名为PDFCreator并使其成为NSObject的子类。 确保将语言设置为Swift

最后,单击Next,然后单击Create

1. Importing PDFKit

要使用PDFKit,您需要将其导入到类中。 在文件顶部的import UIKit下添加以下内容:

import PDFKit

现在,添加这个新方法来创建PDFPDFCreator

func createFlyer() -> Data {
  // 1
  let pdfMetaData = [
    kCGPDFContextCreator: "Flyer Builder",
    kCGPDFContextAuthor: "raywenderlich.com"
  ]
  let format = UIGraphicsPDFRendererFormat() 
  format.documentInfo = pdfMetaData as [String: Any]

  // 2
  let pageWidth = 8.5 * 72.0
  let pageHeight = 11 * 72.0
  let pageRect = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)

  // 3
  let renderer = UIGraphicsPDFRenderer(bounds: pageRect, format: format)
  // 4
  let data = renderer.pdfData { (context) in
    // 5
    context.beginPage()
    // 6
    let attributes = [
      NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 72)
    ]
    let text = "I'm a PDF!"
    text.draw(at: CGPoint(x: 0, y: 0), withAttributes: attributes)
  }

  return data
}

这是代码的作用:

  • 1) 使用预定义键创建包含PDF元数据的字典。您可以在Apple的辅助字典键文档Auxiliary Dictionary Keys中找到完整的键列表。要设置元数据,请创建UIGraphicsPDFRendererFormat对象并将字典分配给documentInfo
  • 2) 回想一下,PDF文件使用每英寸72点的坐标系。要创建具有特定大小的PDF文档,请将大小(以英寸为单位)乘以72以获得点数。在这里,您将使用8.5 x 11英寸,因为这是标准的美国信件大小。然后创建一个刚刚计算出的大小的矩形。
  • 3) UIGraphicsPDFRenderer(bounds:format :)创建一个PDFRenderer对象,其中包含矩形的尺寸和包含元数据的格式。
  • 4) pdfData(actions :)包含一个用于创建PDF的block块。渲染器创建Core Graphics上下文,该上下文成为块中的当前上下文。在此上下文中完成的绘图将显示在PDF上。
  • 5) context.beginPage()启动一个新的PDF页面。在给出任何其他绘制命令之前,您必须先调用beginPage()一次。您可以再次调用它来创建多页PDF文档。
  • 6) 在String上使用draw(at:withAttributes :)将字符串绘制到当前上下文。您将字符串的大小设置为72磅。

现在您有一个简单的PDF,您将在应用程序中设置PDF的预览,以便您可以查看它。


Adding a PDF Preview

入门应用程序已包含PDFView以供预览,但尚未准备好使用。 要开始设置预览,请打开PDFPreviewViewController.swift。 您将把PDF作为Data对象传递给类。 在PDFPreviewViewController的顶部,添加一个变量:

public var documentData: Data?

现在在viewDidLoad()的末尾添加以下代码:

if let data = documentData {
  pdfView.document = PDFDocument(data: data)
  pdfView.autoScales = true
}

加载预览时,如果设置了documentData,则PDFDocument(data :)会根据传递的数据创建PDFDocument

PDFDocument是表示PDF数据的PDFKit对象。 将document分配到PDFView视图,并将document设置为缩放以适合视图。

返回到FlyerBuilderViewController.swift并将以下方法添加到FlyerBuilderViewController的末尾。 确保将其添加到类而不是扩展名。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  if segue.identifier == "previewSegue" {
    guard let vc = segue.destination as? PDFPreviewViewController else { return }
    let pdfCreator = PDFCreator()
    vc.documentData = pdfCreator.createFlyer()
  }
}

当用户在Preview按钮上调用segue时,此方法将创建PDF。 它将PDF数据传递到目标PDFPreviewViewController

现在,构建并运行应用程序。 单击工具栏中的Preview,您将看到在应用程序中创建的PDF文件的预览。

如前所述,PDF的坐标来自左下角。 您可能会惊讶地看到PDF文件顶部的文本,因为它是在坐标(x:0,y:0)处绘制的。 为什么会这样?

pdfData(actions :)创建一个Core Graphics上下文。 PDF坐标和Core Graphics共享相同的点大小,但Core Graphics上下文的坐标从左上角开始,向下和向右增加。

如果需要在坐标空间之间进行转换,PDFKit中的方法会执行此操作。


Adding User Text to the PDF

现在是时候开始实现传单了。 打开PDFCreator.swift并在PDFCreator的顶部添加以下行。

let title: String
let body: String
let image: UIImage
let contactInfo: String

init(title: String, body: String, image: UIImage, contact: String) {
  self.title = title
  self.body = body
  self.image = image
  self.contactInfo = contact
}

这会添加属性以存储传单的标题,正文,图像和联系信息。 您还可以定义自定义初始化方法来设置变量。

由于您拥有PDF的标题,因此现在可以通过打开PDFCreator.swift并将pdfMetaData更新为以下内容将其添加到元数据中:

let pdfMetaData = [
  kCGPDFContextCreator: "Flyer Builder",
  kCGPDFContextAuthor: "raywenderlich.com",
  kCGPDFContextTitle: title
]

Adding the Title With Core Text

您希望传单的标题显示在PDF页面的中心和顶部附近。 为了使创建代码更易于遵循和维护,您将为传单的每个部分创建单独的方法。

添加文本时,您将希望利用Core Text提供的其他布局功能。 因此,在PDFCreator的底部添加以下新方法:

func addTitle(pageRect: CGRect) -> CGFloat {
  // 1
  let titleFont = UIFont.systemFont(ofSize: 18.0, weight: .bold)
  // 2
  let titleAttributes: [NSAttributedString.Key: Any] = 
    [NSAttributedString.Key.font: titleFont]
  // 3
  let attributedTitle = NSAttributedString(
    string: title, 
    attributes: titleAttributes
  ) 
  // 4
  let titleStringSize = attributedTitle.size()
  // 5
  let titleStringRect = CGRect(
    x: (pageRect.width - titleStringSize.width) / 2.0,
    y: 36, 
    width: titleStringSize.width,
    height: titleStringSize.height
  )
  // 6
  attributedTitle.draw(in: titleStringRect)
  // 7
  return titleStringRect.origin.y + titleStringRect.size.height
}

这是这个方法的作用:

  • 1) 您创建一个大小为18磅的系统字体实例,并以粗体显示。
  • 2) 您创建属性字典并将NSAttributedString.Key.font键设置为此字体。
  • 3) 然后,创建NSAttributedString,其中包含所选字体中标题的文本。
  • 4) 在属性字符串上使用size()会返回一个矩形,该矩形的大小为文本在当前上下文中占据的尺寸。
  • 5) 现在,您可以从页面顶部创建一个36个点,该页面在页面上水平居中。下图显示了如何计算文本居中所需的x坐标。从页面宽度中减去字符串的宽度以计算剩余空间。将此数量除以2会均匀地分割文本每一侧的空间。
  • 6) 在NSAttributedString上使用draw(in :)将其绘制在矩形内。
  • 7) 此代码将矩形的y坐标添加到矩形的高度,以查找矩形底部的坐标,如下图所示。然后代码将此坐标返回给调用者。

您将在本教程后面使用此技术更多地放置文本,因此请确保在继续之前了解它。

您现在需要在创建PDF时调用新方法。 在createFlyer()中,替换此代码:

let attributes = [
  NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 72)
]
let text = "I'm a PDF!"
text.draw(at: CGPoint(x: 0, y: 0), withAttributes: attributes)

调用新方法:

let titleBottom = addTitle(pageRect: pageRect)

接下来,您需要将标题传递给PDFCreator。 因此,打开FlyerBuilderViewController.swift并将prepare(for:sender :)的实现替换为:

guard
  segue.identifier == "previewSegue",
  let vc = segue.destination as? PDFPreviewViewController,
  let title = flyerTextEntry.text
  else {
    return
}

let pdfCreator = PDFCreator(
  title: title,
  body: "",
  image: UIImage(),
  contact: ""
)
vc.documentData = pdfCreator.createFlyer()

PDFCreator初始化程序设置标题并为其余元素提供占位符值。 当您实施传单的每个部分时,您将替换这些。

现在,构建并运行应用程序。 输入标题,然后点按Preview。 您应该看到标题文本位于页面顶部的中心位置。


Adding Paragraph Text

如果您使用该应用程序尝试不同的标题,您可能会注意到一个问题:这种定位文本的方法仅在文本适合单行时才有效。 一个很长的标题将冲出PDF的两侧。

虽然标题通常是一行,但大多数文本将覆盖页面上的几行。 幸运的是,Core Text提供了NSParagraphStyle类来解决这个问题。 现在,您将了解如何将包装文本添加到应用程序。

1. Using NSParagraphStyle

要将包装文本引入您的应用,请将此新方法添加到PDFCreator.swiftPDFCreator的底部:

func addBodyText(pageRect: CGRect, textTop: CGFloat) {
  let textFont = UIFont.systemFont(ofSize: 12.0, weight: .regular)
  // 1
  let paragraphStyle = NSMutableParagraphStyle()
  paragraphStyle.alignment = .natural
  paragraphStyle.lineBreakMode = .byWordWrapping
  // 2
  let textAttributes = [
    NSAttributedString.Key.paragraphStyle: paragraphStyle,
    NSAttributedString.Key.font: textFont
  ]
  let attributedText = NSAttributedString(
    string: body, 
    attributes: textAttributes
  )
  // 3
  let textRect = CGRect(
    x: 10, 
    y: textTop, 
    width: pageRect.width - 20,
    height: pageRect.height - textTop - pageRect.height / 5.0
  )
  attributedText.draw(in: textRect)
}

这与您用于绘制标题的方法的不同:

  • 1) 您创建一个NSMutableParagraphStyle对象来定义文本应如何布局和换行。 自然对齐根据应用程序的本地化设置对齐。 行被设置为在分词(word breaks)时换行。
  • 2) 字典包含除了字体之外还设置段落样式的文本属性。 您可以创建一个NSAttributedString,它结合了文本和格式,就像您对标题所做的那样。
  • 3) 文本的矩形有点不同。 它从左侧偏移10个点并将顶部设置为传递的值。 宽度设置为页面宽度减去每边10个边距。 高度是从顶部到页面高度的1/5到底部的距离。

下一步是更新createFlyer()以添加正文文本。 您还将在标题和正文之间添加半英寸的空格。 在调用addTitle()的下方,添加以下内容:

addBodyText(pageRect: pageRect, textTop: titleBottom + 36.0)

接下来,您需要将传单的文本传递给预览控制器。 为此,打开Flyer Builder ViewController.swift并将prepare(for:sender :)的实现替换为:

guard
  segue.identifier == "previewSegue",
  let vc = segue.destination as? PDFPreviewViewController,
  let title = flyerTextEntry.text,
  let body = bodyTextView.text
  else {
    return
}

let pdfCreator = PDFCreator(
  title: title,
  body: body,
  image: UIImage(),
  contact: ""
)
vc.documentData = pdfCreator.createFlyer()

构建并运行您的应用程序。 输入传单正文的标题和一些文本,然后点击预览。 看,您的PDF现在显示两种类型的文本!

您的下一个目标是让用户能够将图像添加到传单中。


Adding Images

启动项目允许用户从照片库或相机中选择图像。 所选图像可以是水平的或垂直的,并且可以是任何尺寸。 将图像添加到页面需要调整图像大小,同时保留图像的宽高比和方向。

首先,在PDFCreator的末尾添加以下方法:

func addImage(pageRect: CGRect, imageTop: CGFloat) -> CGFloat {
  // 1
  let maxHeight = pageRect.height * 0.4
  let maxWidth = pageRect.width * 0.8
  // 2
  let aspectWidth = maxWidth / image.size.width
  let aspectHeight = maxHeight / image.size.height
  let aspectRatio = min(aspectWidth, aspectHeight)
  // 3
  let scaledWidth = image.size.width * aspectRatio
  let scaledHeight = image.size.height * aspectRatio
  // 4
  let imageX = (pageRect.width - scaledWidth) / 2.0
  let imageRect = CGRect(x: imageX, y: imageTop,
                         width: scaledWidth, height: scaledHeight)
  // 5
  image.draw(in: imageRect)
  return imageRect.origin.y + imageRect.size.height
}

以下是此代码如何使图像适合页面。

  • 1) 定义图像最多可以是页面高度的40%和页面宽度的80%。
  • 2) 接下来,计算图像的最大宽度与实际宽度的比率,然后对高度执行相同的操作。您可以将这两个比率中较小的一个作为尺寸来调整图像大小。该比率最大化图像的大小,同时确保其符合约束条件。
  • 3) 使用比率计算图像的缩放高度和宽度。
  • 4) 计算水平偏移量以使图像居中,就像之前使用标题文本一样。使用您计算的大小在此坐标处创建一个矩形。
  • 5) 在UIImage上使用draw(in :)绘制图像。此方法缩放图像以适合矩形。最后,将图像底部的坐标返回给调用者,就像使用标题文本一样。

您将在标题和正文之间插入图像以满足传单设计。在createFlyer()中,用以下内容替换对addBodyText(pageRect:textTop :)的调用:

let imageBottom = addImage(pageRect: pageRect, imageTop: titleBottom + 18.0)
addBodyText(pageRect: pageRect, textTop: imageBottom + 18.0)

接下来,您需要将图像传递给PDFCreator。 打开FlyerBuilderViewController.swift并将prepare(for:sender :)的实现替换为:

if 
  let title = flyerTextEntry.text, 
  let body = bodyTextView.text,
  let image = imagePreview.image {
    let pdfCreator = PDFCreator(title: title, body: body,
                                image: image, contact: "")
    vc.documentData = pdfCreator.createFlyer()
}

构建并运行应用程序,您应该在页面上看到您的图像。 尝试不同大小和方向的照片,您可以看到代码如何使它们适合页面。


Drawing Graphics

传单通常包括底部的撕掉区域,这样读者可以随身携带的联系信息。 您的下一步将添加该功能到传单。

你将分两步实施撕下功能。 首先,您将在页面上添加线条以分隔撕下标签。 然后,您将联系人信息添加到每个选项卡。

首先在PDFCreator的末尾添加一个新方法。

// 1
func drawTearOffs(_ drawContext: CGContext, pageRect: CGRect,
                  tearOffY: CGFloat, numberTabs: Int) {
  // 2
  drawContext.saveGState()
  // 3
  drawContext.setLineWidth(2.0)

  // 4
  drawContext.move(to: CGPoint(x: 0, y: tearOffY))
  drawContext.addLine(to: CGPoint(x: pageRect.width, y: tearOffY))
  drawContext.strokePath()
  drawContext.restoreGState()

  // 5
  drawContext.saveGState()
  let dashLength = CGFloat(72.0 * 0.2)
  drawContext.setLineDash(phase: 0, lengths: [dashLength, dashLength])
  // 6
  let tabWidth = pageRect.width / CGFloat(numberTabs)
  for tearOffIndex in 1..<numberTabs {
    // 7
    let tabX = CGFloat(tearOffIndex) * tabWidth
    drawContext.move(to: CGPoint(x: tabX, y: tearOffY))
    drawContext.addLine(to: CGPoint(x: tabX, y: pageRect.height))
    drawContext.strokePath()
  }
  // 7
  drawContext.restoreGState()
}
  • 1) 这种新方法需要几个参数。首先是绘制图形上下文(稍后详细介绍),然后是页面的矩形。您还可以传递选项卡顶部的位置以及要创建的选项卡数量。
  • 2) 您保存图形上下文的当前状态。稍后,您将恢复上下文,撤消两次调用之间所做的所有更改。此配对可在每个步骤开始时保持环境一致。
  • 3) 然后代码将描边线的宽度设置为两个点。
  • 4) 接下来,在传递的高度绘制从页面左侧到右侧的水平线,然后恢复之前保存的状态。
  • 5) 保存当前上下文后,在选项卡之间绘制虚线垂直线。要在Core Graphics中创建虚线,可以定义一个数组,其长度为交替的实线和空段。这里,数组将短划线和空格定义为0.2英寸长。
  • 6) 要以磅为单位计算每个选项卡的宽度,可以将页面宽度除以选项卡数。接下来,循环并绘制每个选项卡之间的虚线。
  • 7) 在循环内,您可以通过将制表符号乘以每行的宽度来计算分隔线的水平位置。然后从选项卡顶部绘制线条,在此处绘制水平线,到页面底部。
  • 8) 绘制完所有线后,恢复图形状态。

您需要Core Graphics上下文传递给此方法。在UIGraphicsPDFRendererContext上使用cgContext来获取一个。

为此,请转到createFlyer()并在addBodyText(pageRect:textTop :)之后添加以下代码:

let context = context.cgContext
drawTearOffs(context, pageRect: pageRect, tearOffY: pageRect.height * 4.0 / 5.0,
             numberTabs: 8)

构建并运行应用程序。 输入传单的标题,正文和图像,然后点击预览。 你应该看到页面底部的撕下线。

你仍然需要自己切割虚线。 应用程序无法完成所有操作。

所以你有撕下的标签,但它们仍然是空白的。 在下一步中,您将向标签添加联系信息。


Adding Rotated Text

当然,您希望文本在拉出标签的长边上运行,因此您需要旋转文本。 为此,请打开PDFCreator.swift并将以下方法添加到类的末尾:

func drawContactLabels(
    _ drawContext: CGContext, 
    pageRect: CGRect, numberTabs: Int) {
  let contactTextFont = UIFont.systemFont(ofSize: 10.0, weight: .regular)
  let paragraphStyle = NSMutableParagraphStyle()
  paragraphStyle.alignment = .natural
  paragraphStyle.lineBreakMode = .byWordWrapping
  let contactBlurbAttributes = [
    NSAttributedString.Key.paragraphStyle: paragraphStyle,
    NSAttributedString.Key.font: contactTextFont
  ]
  let attributedContactText = NSMutableAttributedString(
                                string: contactInfo,
                                attributes: contactBlurbAttributes
                              ) 
  // 1
  let textHeight = attributedContactText.size().height
  let tabWidth = pageRect.width / CGFloat(numberTabs)
  let horizontalOffset = (tabWidth - textHeight) / 2.0
  drawContext.saveGState()
  // 2
  drawContext.rotate(by: -90.0 * CGFloat.pi / 180.0)
  for tearOffIndex in 0...numberTabs {
    let tabX = CGFloat(tearOffIndex) * tabWidth + horizontalOffset
    // 3
    attributedContactText.draw(at: CGPoint(x: -pageRect.height + 5.0, y: tabX))
  }
  drawContext.restoreGState()
}

此方法是用于将正文文本添加到PDF的过程的扩展。要使文本垂直向上运行,请使用旋转变换(rotation transform)。旋转会更改您确定布局的方式。以下是旋转文本引起的更改:

  • 1) 您使用size()来查找在当前上下文中绘制字符串所需的最小大小。旋转交换绘制文本中的水平和垂直元素。因此,您可以使用文本的高度来确定偏移以使文本在选项卡中居中,而不是宽度。
  • 2) 您想要逆时针旋转文本90度。用负角度变换指示逆时针方向。 Core Graphics期望以弧度指定的角度。
  • 3) 旋转也会影响坐标系,如下图所示。该旋转改变轴的方向和坐标增加的方向。原点未移动时,您可以交换X和Y值,因为X坐标现在向下缩小页面,Y坐标在页面右侧增加。

将以下行添加到createFlyer()的末尾以绘制联系信息:

drawContactLabels(context, pageRect: pageRect, numberTabs: 8)

您需要将联系信息发送到PDFCreator。 打开FlyerBuilderViewController.swift和 - 最后一次 - 用以下代码替换prepare(for:sender :)的实现:

guard
  segue.identifier == "previewSegue",
  let vc = segue.destination as? PDFPreviewViewController,
  let title = flyerTextEntry.text,
  let body = bodyTextView.text,
  let image = imagePreview.image,
  let contact = contactTextView.text
  else {
    return
}

let pdfCreator = PDFCreator(
  title: title,
  body: body,
  image: image,
  contact: contact
)
vc.documentData = pdfCreator.createFlyer()

构建并运行您的应用,输入所有字段的信息,然后点击预览。 你应该看到最终的PDF。

您的应用程序会创建一个PDF,但它并没有被困在应用程序中。 因此,在下一步中,您将添加将PDF共享到应用程序的功能。


Sharing the PDF

打开FlyerBuilderViewController.swift并将shareAction()的内容替换为:

// 1
guard 
  let title = flyerTextEntry.text, 
  let body = bodyTextView.text,
  let image = imagePreview.image, 
  let contact = contactTextView.text 
  else {
    // 2
    let alert = UIAlertController(
      title: "All Information Not Provided",
      message: "You must supply all information to create a flyer.",
      preferredStyle: .alert
    )
      alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
      present(alert, animated: true, completion: nil)
      return
  }
// 3
let pdfCreator = PDFCreator(
  title: title,
  body: body, 
  image: image, 
  contact: contact
) 
let pdfData = pdfCreator.createFlyer()
let vc = UIActivityViewController(
  activityItems: [pdfData],
  applicationActivities: []
) 
present(vc, animated: true, completion: nil)

在这里你:

  • 1) 首先,确保用户添加了传单的所有信息。
  • 2) 如果没有,则创建提示消息,将其显示给用户并返回。
  • 3) 如果用户已添加传单的所有信息,则创建PDF。 然后创建一个UIActivityViewController来提供和显示共享对象的数据。

构建并运行应用程序,输入所有信息,然后点击应用程序右下角的“共享”。 您将看到共享PDF文件的选项。 不幸的是,模拟器没有提供许多有用的选项,因此您可能希望在真实设备上运行该应用程序。


One Final Touch

在共享PDF之前进行的检查是一个很好的用户界面功能。 因此,现在您将添加一个检查,以确保在用户请求预览时所有信息都存在。

打开FlyerBuilderViewController.swift文件,并在prepare(for:sender)上面,添加这个新方法以确定是否应该发生segue

override func shouldPerformSegue(withIdentifier identifier: String,
                                 sender: Any?) -> Bool {
  if 
    let _ = flyerTextEntry.text, 
    let _ = bodyTextView.text,
    let _ = imagePreview.image, 
    let _ = contactTextView.text { 
      return true 
    }

  let alert = UIAlertController(
    title: "All Information Not Provided",
    message: "You must supply all information to create a flyer.", 
    preferredStyle: .alert
  )
  alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
  present(alert, animated: true, completion: nil)

  return false
}

此代码检查用户是否在每个text field中输入了内容并选择了图像。如果是这样,它允许segue。如果没有,它会显示错误消息并停止segue

在本教程中,您构建了一个使用PDFKit创建PDF文件的应用程序。用户的输入会生成一个传单,您可以与其他iOS应用程序共享。在此过程中,您学习了如何创建PDF,使用Core TextCore Graphics并共享创建的文档。

有关PDFKit的更多信息,请查看Apple的PDFKit DocumentationIntroducing PDFKit on iOS — WWDC 2017会议。

后记

本篇主要讲述了基于PDFKit的PDF文档的创建和分享,感兴趣的给个赞或者关注~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容