Creating a PDF in Swift with PDFKit | raywenderlich.com

Learn how to create a PDF, work with Core Text and Core Graphics and share the created document by building an app that displays the user’s input on a flyer that can be shared with other iOS apps.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/4023941-creating-a-pdf-in-swift-with-pdfkit
2 Likes

How to add a second page?

You call context.beginPage() again to start a new page.

1 Like

Great tutorial! One question, how would I add a paragraph with a dynamic height in relation to the pages width, not the attributed text size() width? The size() function only returns the size with a crazy long width. Thanks in advance

Look at the .size(withAttributes:) on String. I think it will do what you’re looking for.

You also may want to take a look at the tutorial at https://www.raywenderlich.com/578-core-text-tutorial-for-ios-making-a-magazine-app. It goes into CoreText formatting and layout in more depth.

Is there a simple way to add a hyperlink?

1 Like

@camrawne You should be able to use an NSAttributedString and set the NSAttributedString.Key.link attribute on it.

1 Like

Really appreciate this tutorial! I’m using paintCode to build graphics for a complex pdf form. Naturally everything is upside down because of the different coordinate systems. You mentioned that PDFKit has some tools to help with the conversion. Can you point me in the right direction for those?

@vegepilot I’m happy to hear you found the tutorial helpful. I think what you’re looking for is userSpaceToDeviceSpaceTransform. You can find more information about it in the Apple documentation for UIGraphicsPDFRenderer at Apple Developer Documentation

In the area of adding images, this is what it says to do.

Next, you need to pass the image to PDFCreator. Open FlyerBuilderViewController.swift and replace the implementation of prepare(for:sender:) with:

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()
}

Here is what I have before replacing with above which compiles.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "previewSegue" {
      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: "",
        image: UIImage(),
        contact: ""
      )
      vc.documentData = pdfCreator.createFlyer()
    }
  }

To replace it and make it work you need to leave in the line

let vc = segue.destination as? PDFPreviewViewController,

Which it then should look like this

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
   if
      let vc = segue.destination as? PDFPreviewViewController,
      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()
    }

  }

And just a note, the Final file in the downloaded materials looks nothing like the actual completed app.

@bmorefield Can you please help with this when you get a chance? Thank you - much appreciated! :]

@jel111 I think you’re right the wording is a bit off there. As you noted it should be to replace part of the guard statement and not the entire method.

1 Like

I get a Build time error running this that Cycle inside FlyerBuilder; building could produce unreliable results Cycle details …

What does this mean?

Hmm, those errors usually relate to the build process. I checked against the latest Xcode and don’t see the error. Can you provide a bit more detail or any changes you might have made to the project?

I made no changes to the code just tried to compile and run it.
I did find a article that said this was related to a build setting issue which seemed to allow it to work. Can’t remember what it was off top of my head. But as soon as I’m back at desk I’ll look it up.

@rpemberton Do you still have issues with this?

Is it applicable to MacOS? Any alternative to UIGraphicsPDFRendererFormat() to use in macOS. macOS cannot identify it. Thanks in advance for the great tutorial

It’s focused on UIKit and would work using Catalyst. For the macOS, look at PDFDocument.

Hi,

I really love this tutorial.
I am wondering how you would create a multipage PDF using an array of (for example) images. Where each image will be on it’s own page. Are you please able to help out?

@heide You’d need to loop through the images, create a new page for each, then use the code from the tutorial on drawing an image to a page.