CanvasView overlay on PDFKit loses quality when zoomed – how to preserve drawing resolution?

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!

CanvasView overlay on PDFKit loses quality when zoomed – how to preserve drawing resolution?
 
 
Q