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!