Swift version: 5.10
Attributed strings contain all the formatting they need to go straight to images, PDFs, and other visual output, although it does take a little setup to get a good PDF out.
First, create your attributed string:
let attributedString = NSAttributedString(string: "This is a test", attributes: [NSAttributedString.Key.foregroundColor: UIColor.red])
Next, wrap that inside a UISimpleTextPrintFormatter
, which is responsible for layout out that string over as many pages as needed:
let printFormatter = UISimpleTextPrintFormatter(attributedText: attributedString)
You can then put that formatter inside a page renderer, telling it to start printing at page 0:
let renderer = UIPrintPageRenderer()
renderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)
Next you need to define a few sizes: how big your paper size is, along with what margins you want.
// A4 size
let pageSize = CGSize(width: 595.2, height: 841.8)
// Use this to get US Letter size instead
// let pageSize = CGSize(width: 612, height: 792)
// create some sensible margins
let pageMargins = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)
// calculate the printable rect from the above two
let printableRect = CGRect(x: pageMargins.left, y: pageMargins.top, width: pageSize.width - pageMargins.left - pageMargins.right, height: pageSize.height - pageMargins.top - pageMargins.bottom)
// and here's the overall paper rectangle
let paperRect = CGRect(x: 0, y: 0, width: pageSize.width, height: pageSize.height)
You can now pass the paper and printable rectangles to the page renderer, like this:
renderer.setValue(NSValue(cgRect: paperRect), forKey: "paperRect")
renderer.setValue(NSValue(cgRect: printableRect), forKey: "printableRect")
The next step is to create an empty instance of NSMutableData
, then ask UIKit to render into that data object:
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, paperRect, nil)
renderer.prepare(forDrawingPages: NSMakeRange(0, renderer.numberOfPages))
Now all that remains is to render draw each page into the bounds of the PDF context, like this:
let bounds = UIGraphicsGetPDFContextBounds()
for i in 0 ..< renderer.numberOfPages {
UIGraphicsBeginPDFPage()
renderer.drawPage(at: i, in: bounds)
}
UIGraphicsEndPDFContext()
At this point your pdfData
value contains the finished PDF, so you can write it wherever you want:
do {
try pdfData.write(to: yourURL)
} catch {
print(error.localizedDescription)
}
SPONSORED Alex is the iOS & Mac developer’s ultimate AI assistant. It integrates with Xcode, offering a best-in-class Swift coding agent. Generate modern SwiftUI from images. Fast-apply suggestions from Claude 3.5 Sonnet, o3-mini, and DeepSeek R1. Autofix Swift 6 errors and warnings. And so much more. Start your 7-day free trial today!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Available from iOS 4.2
This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.
Link copied to your pasteboard.