I'm experiencing an issue with PDFKit where page.removeAnnotation(annotation) successfully removes the annotation from the page's data structure, but the PDFView no longer updates automatically to reflect the change visually.
Issue Details:
The annotation is removed (verified by checking page.annotations.count)
The PDFView display doesn't refresh to show the removal
This code was working correctly before and suddenly stopped working
No code changes were made on my end
PDFKit
RSS for tagDisplay and manipulate PDF documents in your applications using PDFKit.
Posts under PDFKit tag
34 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hey there, I have a slight problem which is also known on the web, but I couldn't find any answers...
My iOS App is working with documents like pdf and images. It is made to handle invoices and upload them to an external service. The app's job is to compress those pdfs and images. Sadly the PDFKit's PDFDocument increases its size just after importing it with pdfDocument = PDFDocument(url: url) and later exporting it to an pdf file again with data = pdf.dataRepresentation() without ever modifying it.
It increases by server KB after each export. Do you have any alternatives to the renderer dataRepresentation() or a general PDFKit alternative
Thanks
Hello,
We are experiencing on some occasions a wrong behavior with PDFDocument method:
func page(at index: Int) -> PDFPage?
With certain PDF files, this method returns the wrong PDFPage.
This occurs on iOS 18.3, 18.5 and 18.6.2 (an maybe on other versions).
Try this PDF for instance (page 81 is returned when index = 2):
https://drive.google.com/open?id=1MHm2wjfsbWB8OiRmARUMmvODYxp4DIqP&usp=drive_fs
Also, I mention that this doesn't occur systematically with this PDF. When making a copy of this file we don't observe the issue.
Could this be linked some kind of internal cache issue ?
Dear Apple Developer Team,
On iOS 26, the contents of PDF pages appear to be swapped.
Could you please advise if there is a workaround or a planned fix for this issue?
Steps to Reproduce:
Download the attached PDF on iOS 26.
Open the PDF in the Files app.
Tap the PDF to view it in Quick Look.
Navigate to page 5.
Expected Result:
The page number displayed at the bottom should be 5.
Actual Result:
The page number displayed at the bottom is 4.
Issue:
This is not limited to page 5—multiple page contents appear to be swapped.
I have also submitted feedback via Feedback Assistant (FB20743531) on October 20.
Best regards,
Yoshihito Suezawa
Hi everyone,
I faced an issue that on IOS 26 removeAnnotation method doesn't remove annotation. This code worked on previous versions (IOS 18, 17) but suddenly stopped working on IOS 26.
Has anyone faced this issue?
guard let document = await pdfView.document else { return }
for pageIndex in 0..<document.pageCount {
guard let page = document.page(at: pageIndex) else { continue }
let annotations = page.annotations
for annotation in annotations {
page.removeAnnotation(annotation)
}
}
Looking for any method to quickly flatten a PDF without opening Preview and without installing 3 party software. Any ideas?
Save as PDF in Preview works, but I don't want to have to open Preview each time I need to do this.
The Create PDF action which appears in Finder when you select 2 or more PDFs flattens PDFs, but it requires me to select 2 or more files, and I generally don't want to combine PDFs--I simply wish to flatten a PDF.
Most Automator and Shortcuts options I am aware of do not flatten PDFs, and in some cases, strip out form field data from PDFs.
I was able to mark annotations outside the page before iOS 26 update in PDFkit, but after the iOS 26 update i am able to mark the annotations(which are not visible) outside the page and save them on DB, but unable to render(show) them.
My IOS app generates pdf files.
Every time my users open the generated pdf files, the autofill popup jumps out, but my pdf file is NOT for interacting.
I'm here to ask if there's a way to mark my pdf files as "not a form", like in metadata or anywhere else?
Getting this crash after I do this in PDFKit a lot:
PDFSelection *nextSelect = [self.pdfView.document findString:currentSearchString fromSelection:currentSelction withOptions:NSCaseInsensitiveSearch];
if (nextSelect != nil)
{
self.pdfView.currentSelection = nextSelect;
[self.pdfView scrollSelectionToVisible:nil];
}
Which often leads to:
0 CoreFoundation 0x000000019ced4770 __exceptionPreprocess + 176
1 libobjc.A.dylib 0x000000019c9b2418 objc_exception_throw + 88
2 CoreFoundation 0x000000019cfffe10 -[__NSPlaceholderDictionary initWithObjects:forKeys:count:] + 724
3 CoreFoundation 0x000000019cfa1ae4 +[NSDictionary dictionaryWithObjects:forKeys:count:] + 52
4 PDFKit 0x00000001cb56e0fc -[PDFView _axPostPageChangeNotification:] + 348
5 Foundation 0x000000019e6a25e4 __NSFireDelayedPerform + 372
6 CoreFoundation 0x000000019ce92290 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 32
7 CoreFoundation 0x000000019ce91f50 __CFRunLoopDoTimer +
Hello,
I’m using PDFKit to display PDFs with a large number of annotations. However, when there are many annotations, I’m experiencing serious performance issues while zooming in/out with PDFView.
• During pinch zoom, it seems like continuous re-rendering occurs.
• Memory usage spikes dramatically while zooming, then drops back down repeatedly.
• As a result, zooming feels laggy and not smooth.
What I’d like to achieve is the following:
1. Prevent unnecessary re-rendering while zooming is in progress.
2. Trigger a single re-render only once the zoom gesture ends (e.g., in scrollViewDidEndZooming).
3. At the very least, avoid the memory spikes during zoom.
Is there any way to control how annotations are re-drawn during zooming in PDFKit, or to throttle/debounce rendering so it happens only after the gesture completes?
I’d really appreciate any advice from others who have encountered similar issues, or guidance from Apple on the recommended approach.
Thanks in advance!
Hi, I’m currently working with PDFKit and using PDFView to display documents.
I’m adding free-drawing content as annotations, but I’ve noticed that these annotations are always rendered on top of the text, which ends up covering the text and making it hard to read.
What I would like to do is render these drawing annotations behind the text layer, so that the document text remains visible above the drawing.
Has anyone found a way to achieve this, or is there a recommended approach for controlling the annotation rendering order in PDFKit?
Thanks in advance!
Same PDF renders differently when open in Chrome, Safari; Apple Preview, Acrobat.
on Apple Preview, Safari - the PDF appears correctly for a second or two and then appears washed out.
Our app uses Safari to render PDFs and our users are complaining that scanned PDFs are not rendering properly.
How do I fix this issue (Swift, Obj-C)?
Hi!
I'm using PDFKits PdfView to display a PDF file and after several page changes, the background turns black, suddenly (like a big black rectangle). The error occurs in the Books App on the iPad as well and looks similiar to this issue:
https://discussions.apple.com/thread/8627073?sortBy=rank
Anyone got a solution for this?
I ave an application that makes use of charts. I would like to have a button for the user to save the chart as a PDF. I tried to have the button save the PDF to the user's document directory directly. That attempt failed. But I was able to save the PDF to the application sandboxed documents directory. The question is how to programmatically move that file from the application documents folder to the user's general documents folder. So far I have not been able to find a method that will move the PDF file. Any ideas?
I’m attempting to display a PDF file in a visionOS application using the PDFView in PDFKit.
When running on device with visionOS 26, a horizontal solid line appears on some pages, while on other pages, both a horizontal and vertical solid line appear.
These lines do not appear
in Xcode preview canvas (macOS, visionOS)
on device running visionOS 2.5
on Mac running macOS 15.6
I thought that this could possibly be the page breaks, but setting displaysPageBreaks = false did not appear to be effective.
Are there any other settings that could be causing the lines to display?
Code Example
struct ContentView: View {
@State var pdf: PDFDocument? = nil
var body: some View {
PDFViewWrapper(pdf: pdf)
.padding()
}
}
#Preview(windowStyle: .automatic) {
ContentView(pdf: PDFDocument(url: Bundle.main.url(forResource: "SampleApple", withExtension: "pdf")!))
.environment(AppModel())
}
struct PDFViewWrapper: UIViewRepresentable {
let pdf: PDFDocument?
func makeUIView(context: Context) -> PDFView {
let view = PDFView()
view.document = pdf
view.displaysPageBreaks = false
return view
}
func updateUIView(_ uiView: PDFView, context: Context) {
uiView.document = pdf
}
}
Tested with
Xcode Version 16.4 (16F6)
Xcode Version 26.0 beta 5 (17A5295f)
visionOS 2.5
visionOS 26 Beta 5
I
By setting the PKCanvasView background color to blue, I can tell that the PKCanvasView for each PDFPage is created normally, but it does not respond to touch. Specifically, whether it is finger or applepencil, all the responses of the page occur from PDFView(such as zoom and scroll), and PKCanvasView can not draw, please how to solve?
class PDFAnnotatableViewController: UIViewController, PDFViewDelegate {
private let pdfView = PDFView()
private var pdfDocument: PDFDocument?
let file: FileItem
private var userSettings: UserSettings
@Binding var selectedPage: Int
@Binding var currentMode: Mode
@Binding var latestPdfChatResponse: LatestPDFChatResponse
@State private var pdfPageCoordinator = PDFPageCoordinator()
@ObservedObject var userMessage: ChatMessage
init(file: FileItem,
userSettings: UserSettings,
drawDataList: Binding<[DrawDataItem]>,
selectedPage: Binding<Int>,
currentMode: Binding<Mode>,
latestPdfChatResponse: Binding<LatestPDFChatResponse>,
userMessage: ChatMessage) {
self.file = file
self.userSettings = userSettings
self._selectedPage = selectedPage
self._currentMode = currentMode
self._latestPdfChatResponse = latestPdfChatResponse
self.userMessage = userMessage
super.init(nibName: nil, bundle: nil)
DispatchQueue.global(qos: .userInitiated).async {
if let document = PDFDocument(url: file.pdfLocalUrl) {
DispatchQueue.main.async {
self.pdfDocument = document
self.pdfView.document = document
self.goToPage(selectedPage: selectedPage.wrappedValue - 1)
}
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
setupPDFView()
}
private func setupPDFView() {
pdfView.delegate = self
pdfView.autoScales = true
pdfView.displayMode = .singlePage
pdfView.displayDirection = .vertical
pdfView.backgroundColor = .white
pdfView.usePageViewController(true)
pdfView.displaysPageBreaks = false
pdfView.displaysAsBook = false
pdfView.minScaleFactor = 0.8
pdfView.maxScaleFactor = 3.5
pdfView.pageOverlayViewProvider = pdfPageCoordinator
if let document = pdfDocument {
pdfView.document = document
goToPage(selectedPage: selectedPage)
}
pdfView.frame = view.bounds
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(pdfView)
NotificationCenter.default.addObserver(
self,
selector: #selector(handlePageChange),
name: .PDFViewPageChanged,
object: pdfView
)
}
// Dealing with page turning
@objc private func handlePageChange(notification: Notification) {
guard let currentPage = pdfView.currentPage, let document = pdfView.document else { return }
let currentPageIndex = document.index(for: currentPage)
if currentPageIndex != selectedPage - 1 {
DispatchQueue.main.async {
self.selectedPage = currentPageIndex + 1
}
}
}
func goToPage(selectedPage: Int) {
guard let document = pdfView.document else { return }
if let page = document.page(at: selectedPage) {
pdfView.go(to: page)
}
}
// Switch function
func togglecurrentMode(currentMode: Mode){
DispatchQueue.main.async {
if self.currentMode == .none{
self.pdfView.usePageViewController(true)
self.pdfView.isUserInteractionEnabled = true
} else if self.currentMode == .annotation {
if let page = self.pdfView.currentPage {
if let canvasView = self.pdfPageCoordinator.getCanvasView(forPage: page) {
canvasView.isUserInteractionEnabled = true
canvasView.tool = PKInkingTool(.pen, color: .red, width: 20)
canvasView.drawingPolicy = .anyInput
canvasView.setNeedsDisplay()
}
}
}
}
}
}
class MyPDFPage: PDFPage {
var drawing: PKDrawing?
func setDrawing(_ drawing: PKDrawing) {
self.drawing = drawing
}
func getDrawing() -> PKDrawing? {
return self.drawing
}
}
class PDFPageCoordinator: NSObject, PDFPageOverlayViewProvider {
var pageToViewMapping = [PDFPage: PKCanvasView]()
func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? {
var resultView: PKCanvasView? = nil
if let overlayView = pageToViewMapping[page] {
resultView = overlayView
} else {
let canvasView = PKCanvasView(frame: view.bounds)
canvasView.drawingPolicy = .anyInput
canvasView.tool = PKInkingTool(.pen, color: .systemYellow, width: 20)
canvasView.backgroundColor = .blue
pageToViewMapping[page] = canvasView
resultView = canvasView
}
if let page = page as? MyPDFPage, let drawing = page.drawing {
resultView?.drawing = drawing
}
return resultView
}
func pdfView(_ pdfView: PDFView, willEndDisplayingOverlayView overlayView: UIView, for page: PDFPage) {
guard let overlayView = overlayView as? PKCanvasView, let page = page as? MyPDFPage else { return }
page.drawing = overlayView.drawing
pageToViewMapping.removeValue(forKey: page)
}
func savePDFDocument(_ pdfDocument: PDFDocument) -> Data {
for i in 0..<pdfDocument.pageCount {
if let page = pdfDocument.page(at: i) as? MyPDFPage, let drawing = page.drawing {
let newAnnotation = PDFAnnotation(bounds: drawing.bounds, forType: .stamp, withProperties: nil)
let codedData = try! NSKeyedArchiver.archivedData(withRootObject: drawing, requiringSecureCoding: true)
newAnnotation.setValue(codedData, forAnnotationKey: PDFAnnotationKey(rawValue: "drawingData"))
page.addAnnotation(newAnnotation)
}
}
let options = [PDFDocumentWriteOption.burnInAnnotationsOption: true]
if let resultData = pdfDocument.dataRepresentation(options: options) {
return resultData
}
return Data()
}
func getCanvasView(forPage page: PDFPage) -> PKCanvasView? {
return pageToViewMapping[page]
}
}
Is there an error in my code? Please tell me how to make PKCanvasView painting normally?
I had project going great, where i needed to do stuff with pdfs, drawing on top them etc. Since apple is all closed sourced i needed to become a bit hacky. Anyways, i have a problem since the new ios 26 update which breaks the behaviour. I simplified the code very much into a demo project, where you can quickly see what's wrong.
When swiping left to go to the next page, it does change the page etc in the pdf Document, but visually nothing happens. I am stuck on the first page. I dont know what to do, tried a lot of things, but nothing works. Anyone skilled enough to help me out?
import UIKit
import PDFKit
import SwiftUI
class PDFViewController: UIViewController {
var pdfView: PDFView!
var gestureHandler: GestureHandler!
override func viewDidLoad() {
super.viewDidLoad()
setupPDFView()
setupGestureHandler()
loadPDF()
}
private func setupPDFView() {
pdfView = PDFView(frame: view.bounds)
// Your exact configuration
pdfView.autoScales = true
pdfView.pageShadowsEnabled = false
pdfView.backgroundColor = .white
pdfView.displayMode = .singlePage
view.addSubview(pdfView)
// Setup constraints
pdfView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
pdfView.topAnchor.constraint(equalTo: view.topAnchor),
pdfView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
pdfView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
pdfView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
private func setupGestureHandler() {
gestureHandler = GestureHandler(pdfView: pdfView)
gestureHandler.setupSwipeGestures(on: view)
}
private func loadPDF() {
if let path = Bundle.main.path(forResource: "sonate12", ofType: "pdf"),
let document = PDFDocument(url: URL(fileURLWithPath: path)) {
pdfView.document = document
} else {
print("Could not find sonate12.pdf in bundle")
}
}
}
class GestureHandler {
private weak var pdfView: PDFView?
init(pdfView: PDFView) {
self.pdfView = pdfView
}
func setupSwipeGestures(on view: UIView) {
// Left swipe - go to next page
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
leftSwipe.direction = .left
view.addGestureRecognizer(leftSwipe)
// Right swipe - go to previous page
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
rightSwipe.direction = .right
view.addGestureRecognizer(rightSwipe)
}
@objc private func handleSwipe(_ gesture: UISwipeGestureRecognizer) {
guard let pdfView = pdfView,
let document = pdfView.document,
let currentPage = pdfView.currentPage else {
print("🚫 No PDF view, document, or current page available")
return
}
let currentIndex = document.index(for: currentPage)
let totalPages = document.pageCount
print("📄 Current state: Page \(currentIndex + 1) of \(totalPages)")
print("👆 Swipe direction: \(gesture.direction == .left ? "LEFT (next)" : "RIGHT (previous)")")
switch gesture.direction {
case .left:
// Next page
guard currentIndex < document.pageCount - 1 else {
print("🚫 Already on last page (\(currentIndex + 1)), cannot go forward")
return
}
let nextPage = document.page(at: currentIndex + 1)
if let page = nextPage {
print("➡️ Going to page \(currentIndex + 2)")
pdfView.go(to: page)
pdfView.setNeedsDisplay()
pdfView.layoutIfNeeded()
// Check if navigation actually worked
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
if let newCurrentPage = pdfView.currentPage {
let newIndex = document.index(for: newCurrentPage)
print("✅ Navigation result: Now on page \(newIndex + 1)")
if newIndex == currentIndex {
print("⚠️ WARNING: Page didn't change visually!")
}
}
}
} else {
print("🚫 Could not get next page object")
}
case .right:
// Previous page
guard currentIndex > 0 else {
print("🚫 Already on first page (1), cannot go back")
return
}
let previousPage = document.page(at: currentIndex - 1)
if let page = previousPage {
print("⬅️ Going to page \(currentIndex)")
pdfView.go(to: page)
pdfView.setNeedsDisplay()
pdfView.layoutIfNeeded()
let bounds = pdfView.bounds
pdfView.bounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width + 0.01, height: bounds.height)
pdfView.bounds = bounds
// Check if navigation actually worked
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
if let newCurrentPage = pdfView.currentPage {
let newIndex = document.index(for: newCurrentPage)
print("✅ Navigation result: Now on page \(newIndex + 1)")
if newIndex == currentIndex {
print("⚠️ WARNING: Page didn't change visually!")
}
}
}
} else {
print("🚫 Could not get previous page object")
}
default:
print("🤷♂️ Unknown swipe direction")
break
}
}
}
struct PDFViewerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> PDFViewController {
return PDFViewController()
}
func updateUIViewController(_ uiViewController: PDFViewController, context: Context) {
// No updates needed
}
}
You can look at the code here as well: https://github.com/vallezw/swift-bug-ios26
With PDFKit in SwiftUI, I'm using the findString function to search the text of a PDF. That part works correctly, but when I use the extend function to get some of the text on both sides of the found word (ie, its context in the page), it doesn't extend.
Am I doing this wrong? There is very little documentation or examples about the extend function; even the Apple page doesn't specify what the units refer to in the extend call (presumably it means characters, but I suppose it could also be pixels, or some other measurement specific to PDFs).
Here is the code, and pictures of the app, the output (showing that the code can read all the text on the page), and the Acrobat Reader effect I'm trying to achieve.
If the extend function truly is broken, and not just a problem in how I'm going about this, a workaround would be to use the text from the entire page, and extract the surrounding words from there, but that does get complicated, especially if there are multiple instances of the word on the page, or if the result straddles 2 pages.
import PDFKit
import SwiftUI
struct ContentView: View {
@StateObject var controller = PDFViewController()
@State var searchResults:[PDFSelection] = []
var body: some View {
VStack {
Button {
searchResults = controller.pdfView!.document!.findString("is", withOptions: [.caseInsensitive])
let fullPageText = controller.pdfView!.document!.string
print(fullPageText)
for result in searchResults {
let beforeExtending = result.string ?? ""
print("Before: \(beforeExtending)")
result.extend(atEnd: 3)
result.extend(atStart: 3)
let afterExtending = result.string ?? ""
print("After: \(afterExtending)")
}
} label: {
Text("Do search")
}
PDFKitView(url: generateURL(), controller: controller)
}
.padding()
}
func generateURL() -> URL {
let bundlePathRootAsString = Bundle.main.resourcePath!
var pdfPathInBundle = URL(fileURLWithPath: bundlePathRootAsString)
pdfPathInBundle.append(path: "TestPDF.pdf")
return pdfPathInBundle
}
}
struct PDFKitView: UIViewRepresentable {
func updateUIView(_ uiView: PDFView, context: Context) {
}
let url: URL
@ObservedObject var controller: PDFViewController
func makeUIView(context: Context) -> PDFView {
let pdfView = PDFView()
pdfView.document = PDFDocument(url: self.url)
pdfView.autoScales = true
controller.pdfView = pdfView
return pdfView
}
}
class PDFViewController: ObservableObject {
var pdfView: PDFView?
}
I added a canvas view using PDFPageOverlayViewProvider. When I zoom the PDFView, the drawing is scaled, but its quality becomes blurry. How can I fix this?
import SwiftUI
import PDFKit
import PencilKit
import CoreGraphics
struct ContentView: View {
var body: some View {
if
let url = Bundle.main.url(forResource: "sample", withExtension: "pdf"),
let data = try? Data(contentsOf: url),
let document = PDFDocument(data: data)
{
PDFRepresentableView(document: document)
} else {
Text("fail")
}
}
}
#Preview {
ContentView()
}
struct PDFRepresentableView: UIViewRepresentable {
let document: PDFDocument
let pdfView = PDFView()
func makeUIView(context: Context) -> PDFView {
pdfView.displayMode = .singlePageContinuous
pdfView.usePageViewController(false)
pdfView.displayDirection = .vertical
pdfView.pageOverlayViewProvider = context.coordinator
pdfView.document = document
pdfView.autoScales = false
pdfView.minScaleFactor = 0.7
pdfView.maxScaleFactor = 4
return pdfView
}
func updateUIView(_ uiView: PDFView, context: Context) {
// Optional: update logic if needed
}
func makeCoordinator() -> CustomCoordinator {
return CustomCoordinator(parent: self)
}
}
class CustomCoordinator: NSObject, PDFPageOverlayViewProvider, PKCanvasViewDelegate {
let parent: PDFRepresentableView
init(parent: PDFRepresentableView) {
self.parent = parent
}
func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? {
let result = UIView()
let canvasView = PKCanvasView()
canvasView.drawingPolicy = .anyInput
canvasView.tool = PKInkingTool(.pen, color: .blue, width: 20)
canvasView.translatesAutoresizingMaskIntoConstraints = false
result.addSubview(canvasView)
NSLayoutConstraint.activate([
canvasView.leadingAnchor.constraint(equalTo: result.leadingAnchor),
canvasView.trailingAnchor.constraint(equalTo: result.trailingAnchor),
canvasView.topAnchor.constraint(equalTo: result.topAnchor),
canvasView.bottomAnchor.constraint(equalTo: result.bottomAnchor)
])
for subView in view.documentView?.subviews ?? [] {
subView.isUserInteractionEnabled = true
}
result.layoutIfNeeded()
return result
}
}
Hi all,
I’m currently building a SwiftUI app that overlays a PKCanvasView onto each page of a PDFView using PDFPageOverlayViewProvider. It works well at the initial scale, but once I zoom into the PDF, the drawings on the PKCanvasView appear blurry or pixelated, even though the PDF itself remains crisp.
I’m trying to adjust canvasView.contentScaleFactor relative to pdfView.scaleFactor to preserve the drawing quality. Here’s a simplified version of the relevant code:
import SwiftUI
import PDFKit
import PencilKit
struct ContentView: View {
var body: some View {
if let url = Bundle.main.url(forResource: "sample", withExtension: "pdf"),
let data = try? Data(contentsOf: url),
let document = PDFDocument(data: data) {
PDFRepresentableView(document: document)
} else {
Text("")
}
}
}
#Preview {
ContentView()
}
struct PDFRepresentableView: UIViewRepresentable {
let document: PDFDocument
let pdfView = PDFView()
func makeUIView(context: Context) -> PDFView {
pdfView.displayMode = .singlePageContinuous
pdfView.usePageViewController(false)
pdfView.displayDirection = .vertical
pdfView.pageOverlayViewProvider = context.coordinator
pdfView.document = document
pdfView.autoScales = false
pdfView.minScaleFactor = 0.7
pdfView.maxScaleFactor = 4
NotificationCenter.default.addObserver(
context.coordinator,
selector: #selector(context.coordinator.onPageZoomAndPan),
name: .PDFViewScaleChanged,
object: pdfView
)
return pdfView
}
func updateUIView(_ uiView: PDFView, context: Context) {
// Optional: update logic if needed
}
func makeCoordinator() -> CustomCoordinator {
return CustomCoordinator(parent: self)
}
}
class CustomCoordinator: NSObject, PDFPageOverlayViewProvider, PKCanvasViewDelegate {
let parent: PDFRepresentableView
init(parent: PDFRepresentableView) {
self.parent = parent
}
func pdfView(_ view: PDFView, overlayViewFor page: PDFPage) -> UIView? {
let canvasView = PKCanvasView()
let rect = page.bounds(for: .mediaBox)
canvasView.drawingPolicy = .anyInput
canvasView.tool = PKInkingTool(.pen, color: .black, width: 10)
canvasView.translatesAutoresizingMaskIntoConstraints = true
canvasView.backgroundColor = .red.withAlphaComponent(0.1)
canvasView.frame = rect
canvasView.isScrollEnabled = false
for subView in view.documentView?.subviews ?? [] {
subView.isUserInteractionEnabled = true
}
return canvasView
}
@objc func onPageZoomAndPan() {
parent.pdfView.documentView?.subviews.forEach { subview in
if
subview.theClassName == "PDFPageView",
let pageViewPrivate = subview.value(forKey: "_private") as? NSObject,
let page = pageViewPrivate.value(forKey: "page") as? PDFPage {
subview.subviews.forEach { subview in
if let canvasView = subview as? PKCanvasView {
let zoomScale = parent.pdfView.scaleFactor
canvasView.contentScaleFactor = UIScreen.main.scale * zoomScale
canvasView.drawing = canvasView.drawing
canvasView.setNeedsDisplay()
canvasView.layoutIfNeeded()
}
}
}
}
print("Zoom changed. Current scale: \(parent.pdfView.scaleFactor)")
}
}
extension NSObject {
var theClassName: String {
return NSStringFromClass(type(of: self))
}
}
But this doesn’t seem to improve the rendered quality. The lines still appear blurry when zoomed in.
What I’ve tried:
• Adjusting contentScaleFactor and forcing redraw
• Reassigning canvasView.drawing
• Calling setNeedsDisplay() and layoutIfNeeded()
None of these approaches seem to re-render the canvas at a higher resolution or match the zoomed scale of the PDF.
My questions:
1. Is there a correct way to scale PKCanvasView content to match PDF zoom levels?
2. Should I recreate the canvas or drawing when zoom changes?
3. Is PKCanvasView just not intended to handle high zoom fidelity?
If anyone has successfully overlaid high-resolution canvas drawing on a zoomable PDFView, I’d love to hear how you managed it.
Thanks in advance!