Display and manipulate PDF documents in your applications using PDFKit.

Posts under PDFKit tag

28 Posts

Post

Replies

Boosts

Views

Activity

How to fix the gestureRecognizer over PDFKit breaking the paging in the new ios26 version?
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
0
1
318
Aug ’25
PDFKit findString selection extend not working
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? }
2
0
209
Jul ’25
Does the canvas view on top of the PDFView not re-render?
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 } }
1
0
308
Jul ’25
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!
0
0
244
Jul ’25
Files App Share Context with Security scoped resource fails
I'm creating an App that can accepted PDFs from a shared context. I am using iOS, Swift, and UIKit with IOS 17.1+ The logic is: get the context see who is sending in (this is always unknown) see if I can open in place (in case I want to save later) send the URL off to open the (PDF) document and load it into PDFKit's pdfView.document I have no trouble loading PDF docs with the file picker. And everything works as expected for shares from apps like Messages, email, etc... (in which case URLContexts.first.options.openInPlace == False) The problem is with opening (sharing) a PDF that is sent from the Files App. (openInPlace == True) If the PDF is in the App's Document Folder, I need the Security scoped resource, to access the URL from the File's App so that I can copy the PDF's data to the PDFViewer.document. I get Security scoped resource access granted each time I get the File App's context URL. But, when I call fileCoordinator.coordinate and try to access a file outside of the App's document folder using the newUrl, I get an error. FYI - The newUrl (byAccessor) and context url (readingItemAt) paths are always same for the Files App URL share context. I can, however, copy the file to a new location in my apps directory and then open it from there and load in the data. But I really do not want to do that. . . . . . Questions: Am I missing something in my pList or are there other parameters specific to sharing a file from the Files App? I'd appreciate if someone shed some light on this? . . . . . Here are the parts of my code related to this with some print statements... . . . . . SceneDelegate func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { // nothing to see here, move along guard let urlContext = URLContexts.first else { print("No URLContext found") return } // let's get the URL (it will be a PDF) let url = urlContext.url let openInPlace = urlContext.options.openInPlace let bundleID = urlContext.options.sourceApplication print("Triggered with URL: \(url)") print("Can Open In Place?: \(openInPlace)") print("For Bundle ID: \(bundleID ?? "None")") // get my Root ViewController from window if let rootViewController = self.window?.rootViewController { // currently using just the view if let targetViewController = rootViewController as? ViewController { targetViewController.prepareToLoadSharedPDFDocument(at: url) } // I might use a UINavigationController in the future else if let navigationController = rootViewController as? UINavigationController, let targetViewController = navigationController.viewControllers.first as? ViewController { targetViewController.prepareToLoadSharedPDFDocument(at: url) } } } . . . . ViewController function I broke out the if statement for accessingScope just to make it easier for me the debug and play around with the code in accessingScope == True func loadPDF(fromUrl url: URL) { // If using the File Picker / don't use this // If going through a Share.... we pass the URL and have three outcomes (1, 2a, 2b) // 1. Security scoped resource access NOT needed if from a Share Like Messages or EMail // 2. Security scoped resource access granted/needed from 'Files' App // a. success if in the App's doc directory // b. fail if NOT in the App's doc directory // Set the securty scope variable var accessingScope = false // Log the URLs for debugging print("URL String: \(url.absoluteString)") print("URL Path: \(url.path())") // Check if the URL requires security scoped resource access if url.startAccessingSecurityScopedResource() { accessingScope = true print("Security scoped resource access granted.") } else { print("Security scoped resource access denied or not needed.") } // Stop accessing the scope once everything is compeleted defer { if accessingScope { url.stopAccessingSecurityScopedResource() print("Security scoped resource access stopped.") } } // Make sure the file is still there (it should be in this case) guard FileManager.default.fileExists(atPath: url.path) else { print("File does not exist at URL: \(url)") return } // Let's see if we can open it in place if accessingScope { let fileCoordinator = NSFileCoordinator() var error: NSError? fileCoordinator.coordinate(readingItemAt: url, options: [], error: &error) { (newUrl) in DispatchQueue.main.async { print(url.path()) print(newUrl.path()) if let document = PDFDocument(url: newUrl) { self.pdfView.document = document self.documentFileName = newUrl.deletingPathExtension().lastPathComponent self.fileLoadLocation = newUrl.path() self.updateGUI(pdfLoaded: true) self.setPDFScale(to: self.VM.pdfPageScale, asNewPDF: true) } else { print("Could not load PDF directly from url: \(newUrl)") } } } if let error = error { PRINT("File coordination error: \(error)") } } else { DispatchQueue.main.async { if let document = PDFDocument(url: url) { self.pdfView.document = document self.documentFileName = url.deletingPathExtension().lastPathComponent self.fileLoadLocation = url.path() self.updateGUI(pdfLoaded: true) self.setPDFScale(to: self.VM.pdfPageScale, asNewPDF: true) } else { PRINT("Could not load PDF from url: \(url)") } } } } . . . . Other relevant pList settings I've added are: Supports opening documents in place - YES Document types - PDFs (com.adobe.pdf) UIDocumentBrowserRecentDocumentContentTypes - com.adobe.pdf Application supports iTunes file sharing - YES And iCloud is one for Entitlements with iCloud Container Identifiers Ubiquity Container Identifiers . . . . Thank you in advance!. B
2
1
847
Jul ’25
PDF in WebView
Dear all, Is it possible to replace the default PDF background colour the 50% grey to any other colour while using the new WebView? Using the standard .background method on WebView does not appear to have any effect: WebView(pdfWebpage) .background(Color.blue) // no effect on the background of the PDF Thanks!
1
0
188
Jun ’25
PDFKit PDFPage.characterBounds(at:) returns incorrect coordinates iOS 18 beta 4
PDFKit PDFPage.characterBounds(at: Int) is returning incorrect coordinates with iOS 18 beta 4 and later / Xcode 16 beta 4. It worked fine in iOS 17 and earlier (after showing the same issue during the early iOS 17 beta cycle) It breaks critical functionality that my app relies on. I have filed feedback (FB14843671). So far no changes in the latest betas. iOS release date is approaching fast! Anybody having the same issue? Any workaround available?
17
3
2.1k
May ’25
Come forzare la visualizzazione di un font diverso (es. Helvetica su iOS, Arial su Windows) in un PDF non incorporando i caratteri
Sto cercando di creare un PDF che, a seconda del sistema operativo, utilizzi un font diverso. Visto che mi è capitato di scaricare da Internet un PDF che veniva visualizzato con con Arial su Windows e con Helvetica su iOS/macOS (anche su siti di drive, OneDrive ) vorrei creare un PDF che venga visualizzato con Arial su Windows e con Helvetica su iOS/macOS, sfruttando i meccanismi di fallback dei font di sistema (senza incorporare i font nel PDF). Ho provato a: • Scrivere il documento in Arial da Word su Windows; • Scrivere il documento in Helvetica da Word su Windows; • Disattivare l’incorporamento dei font nel salvataggio PDF su “Word”; Tuttavia, su iOS , in app come Onedrive, il PDF continua a visualizzarsi in Arial C’è un modo per: Evitare che iOS usi Arial se presente? Far sì che venga usato Helvetica come fallback? mi interessa anche capire se si può impedire ad iOS di usare Arial in lettura PDF. Qualcuno ha affrontato un caso simile o conosce un modo affidabile per ottenere questo comportamento cross-platform?
1
0
152
Apr ’25
How to fix the gestureRecognizer over PDFKit breaking the paging in the new ios26 version?
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
Replies
0
Boosts
1
Views
318
Activity
Aug ’25
PDFKit findString selection extend not working
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? }
Replies
2
Boosts
0
Views
209
Activity
Jul ’25
Does the canvas view on top of the PDFView not re-render?
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 } }
Replies
1
Boosts
0
Views
308
Activity
Jul ’25
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!
Replies
0
Boosts
0
Views
244
Activity
Jul ’25
Files App Share Context with Security scoped resource fails
I'm creating an App that can accepted PDFs from a shared context. I am using iOS, Swift, and UIKit with IOS 17.1+ The logic is: get the context see who is sending in (this is always unknown) see if I can open in place (in case I want to save later) send the URL off to open the (PDF) document and load it into PDFKit's pdfView.document I have no trouble loading PDF docs with the file picker. And everything works as expected for shares from apps like Messages, email, etc... (in which case URLContexts.first.options.openInPlace == False) The problem is with opening (sharing) a PDF that is sent from the Files App. (openInPlace == True) If the PDF is in the App's Document Folder, I need the Security scoped resource, to access the URL from the File's App so that I can copy the PDF's data to the PDFViewer.document. I get Security scoped resource access granted each time I get the File App's context URL. But, when I call fileCoordinator.coordinate and try to access a file outside of the App's document folder using the newUrl, I get an error. FYI - The newUrl (byAccessor) and context url (readingItemAt) paths are always same for the Files App URL share context. I can, however, copy the file to a new location in my apps directory and then open it from there and load in the data. But I really do not want to do that. . . . . . Questions: Am I missing something in my pList or are there other parameters specific to sharing a file from the Files App? I'd appreciate if someone shed some light on this? . . . . . Here are the parts of my code related to this with some print statements... . . . . . SceneDelegate func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { // nothing to see here, move along guard let urlContext = URLContexts.first else { print("No URLContext found") return } // let's get the URL (it will be a PDF) let url = urlContext.url let openInPlace = urlContext.options.openInPlace let bundleID = urlContext.options.sourceApplication print("Triggered with URL: \(url)") print("Can Open In Place?: \(openInPlace)") print("For Bundle ID: \(bundleID ?? "None")") // get my Root ViewController from window if let rootViewController = self.window?.rootViewController { // currently using just the view if let targetViewController = rootViewController as? ViewController { targetViewController.prepareToLoadSharedPDFDocument(at: url) } // I might use a UINavigationController in the future else if let navigationController = rootViewController as? UINavigationController, let targetViewController = navigationController.viewControllers.first as? ViewController { targetViewController.prepareToLoadSharedPDFDocument(at: url) } } } . . . . ViewController function I broke out the if statement for accessingScope just to make it easier for me the debug and play around with the code in accessingScope == True func loadPDF(fromUrl url: URL) { // If using the File Picker / don't use this // If going through a Share.... we pass the URL and have three outcomes (1, 2a, 2b) // 1. Security scoped resource access NOT needed if from a Share Like Messages or EMail // 2. Security scoped resource access granted/needed from 'Files' App // a. success if in the App's doc directory // b. fail if NOT in the App's doc directory // Set the securty scope variable var accessingScope = false // Log the URLs for debugging print("URL String: \(url.absoluteString)") print("URL Path: \(url.path())") // Check if the URL requires security scoped resource access if url.startAccessingSecurityScopedResource() { accessingScope = true print("Security scoped resource access granted.") } else { print("Security scoped resource access denied or not needed.") } // Stop accessing the scope once everything is compeleted defer { if accessingScope { url.stopAccessingSecurityScopedResource() print("Security scoped resource access stopped.") } } // Make sure the file is still there (it should be in this case) guard FileManager.default.fileExists(atPath: url.path) else { print("File does not exist at URL: \(url)") return } // Let's see if we can open it in place if accessingScope { let fileCoordinator = NSFileCoordinator() var error: NSError? fileCoordinator.coordinate(readingItemAt: url, options: [], error: &error) { (newUrl) in DispatchQueue.main.async { print(url.path()) print(newUrl.path()) if let document = PDFDocument(url: newUrl) { self.pdfView.document = document self.documentFileName = newUrl.deletingPathExtension().lastPathComponent self.fileLoadLocation = newUrl.path() self.updateGUI(pdfLoaded: true) self.setPDFScale(to: self.VM.pdfPageScale, asNewPDF: true) } else { print("Could not load PDF directly from url: \(newUrl)") } } } if let error = error { PRINT("File coordination error: \(error)") } } else { DispatchQueue.main.async { if let document = PDFDocument(url: url) { self.pdfView.document = document self.documentFileName = url.deletingPathExtension().lastPathComponent self.fileLoadLocation = url.path() self.updateGUI(pdfLoaded: true) self.setPDFScale(to: self.VM.pdfPageScale, asNewPDF: true) } else { PRINT("Could not load PDF from url: \(url)") } } } } . . . . Other relevant pList settings I've added are: Supports opening documents in place - YES Document types - PDFs (com.adobe.pdf) UIDocumentBrowserRecentDocumentContentTypes - com.adobe.pdf Application supports iTunes file sharing - YES And iCloud is one for Entitlements with iCloud Container Identifiers Ubiquity Container Identifiers . . . . Thank you in advance!. B
Replies
2
Boosts
1
Views
847
Activity
Jul ’25
PDF in WebView
Dear all, Is it possible to replace the default PDF background colour the 50% grey to any other colour while using the new WebView? Using the standard .background method on WebView does not appear to have any effect: WebView(pdfWebpage) .background(Color.blue) // no effect on the background of the PDF Thanks!
Replies
1
Boosts
0
Views
188
Activity
Jun ’25
PDFKit PDFPage.characterBounds(at:) returns incorrect coordinates iOS 18 beta 4
PDFKit PDFPage.characterBounds(at: Int) is returning incorrect coordinates with iOS 18 beta 4 and later / Xcode 16 beta 4. It worked fine in iOS 17 and earlier (after showing the same issue during the early iOS 17 beta cycle) It breaks critical functionality that my app relies on. I have filed feedback (FB14843671). So far no changes in the latest betas. iOS release date is approaching fast! Anybody having the same issue? Any workaround available?
Replies
17
Boosts
3
Views
2.1k
Activity
May ’25
Come forzare la visualizzazione di un font diverso (es. Helvetica su iOS, Arial su Windows) in un PDF non incorporando i caratteri
Sto cercando di creare un PDF che, a seconda del sistema operativo, utilizzi un font diverso. Visto che mi è capitato di scaricare da Internet un PDF che veniva visualizzato con con Arial su Windows e con Helvetica su iOS/macOS (anche su siti di drive, OneDrive ) vorrei creare un PDF che venga visualizzato con Arial su Windows e con Helvetica su iOS/macOS, sfruttando i meccanismi di fallback dei font di sistema (senza incorporare i font nel PDF). Ho provato a: • Scrivere il documento in Arial da Word su Windows; • Scrivere il documento in Helvetica da Word su Windows; • Disattivare l’incorporamento dei font nel salvataggio PDF su “Word”; Tuttavia, su iOS , in app come Onedrive, il PDF continua a visualizzarsi in Arial C’è un modo per: Evitare che iOS usi Arial se presente? Far sì che venga usato Helvetica come fallback? mi interessa anche capire se si può impedire ad iOS di usare Arial in lettura PDF. Qualcuno ha affrontato un caso simile o conosce un modo affidabile per ottenere questo comportamento cross-platform?
Replies
1
Boosts
0
Views
152
Activity
Apr ’25