I have three buttons in my swiftui view..
- clear(clear current page);
- clear all;
- submit.
when a user annotates the pdf (ink) and clear the annotation and than decides to draw(annotate) again. The old annotations which were cleared reappear. I don't want the old annotations to reappear once they are cleared.
same bug is faced for clear all function.
once a page annotations are clear the old annotations should not come. and once all annotations are cleared non of the old annotations should not come.
In my other view where I display the pdf using the pdf container on button press I call the following functions:
clear all : self.myLocalCoordinator?.removeAllAnnotations()
clear: self.myLocalCoordinator?.removeAnnotationsOnCurrentPage()
submit: if let base64PDF = myLocalCoordinator?.savePDFWithAnnotations() { print("PDF signed and annotated: (base64PDF)")
}
myLocalCoordinator? is just a reference to the coordinator class in my view which I get from the completion handler.
Below is what I tried:
import SwiftUI
import PDFKit
import PencilKit
import UIKit
struct PDFViewContainerMulti: UIViewRepresentable {
var base64Data: String
@Binding var currentPageIndex: Int
var completionHandler: (Int, Coordinator) -> Void
let pdfView = PDFView()
// var coordinator : Coordinator
func makeUIView(context: Context) -> PDFView {
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
if let data = Data(base64Encoded: base64Data) {
if let pdfDocument = PDFDocument(data: data) {
pdfView.document = pdfDocument
pdfView.autoScales = true
//pdfView.displayMode = .singlePageContinuous
pdfView.displayMode = .singlePage
// Enable user interactions to allow annotation
//pdfView.isUserInteractionEnabled = false
let totalNumberOfPages = pdfDocument.pageCount
print("The total pages in the pdf document is : \(totalNumberOfPages)")
completionHandler(pdfDocument.pageCount,Coordinator(parent: self))
if currentPageIndex < pdfDocument.pageCount {
pdfView.go(to: pdfDocument.page(at: currentPageIndex)!)
}
// Set up gesture recognizer for drawing
let panGestureRecognizer = UIPanGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handlePanGesture(_:)))
pdfView.addGestureRecognizer(panGestureRecognizer)
}
}
if let scrollView = pdfView.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView {
scrollView.isScrollEnabled = false // Disable default scrolling
}
// context.coordinator.pdfView = pdfView
// coordinator.pdfView = context.coordinator.pdfView
PdfView().pdfView = pdfView
return pdfView
}
func updateUIView(_ pdfView: PDFView, context: Context) {
// Update the view if needed
if currentPageIndex < pdfView.document?.pageCount ?? 0 {
pdfView.go(to: pdfView.document!.page(at: currentPageIndex)!)
}
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
}
class Coordinator: NSObject, ObservableObject {
var parent: PDFViewContainerMulti
var allPathsDict: [PDFPage: [UIBezierPath]] = [:]
var shouldDraw = false
var pdfView: PDFView?
init(parent: PDFViewContainerMulti) {
self.parent = parent
self.pdfView = parent.pdfView
}
// @objc func handlePanGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
// let location = gestureRecognizer.location(in: gestureRecognizer.view)
//
// if let pdfView = gestureRecognizer.view as? PDFView {
// if let currentPage = pdfView.page(for: location, nearest: true) {
// let convertedPoint = pdfView.convert(location, to: currentPage)
//
// switch gestureRecognizer.state {
// case .began:
// shouldDraw = isPointWithinDrawingArea(convertedPoint, in: currentPage, of: pdfView)
// if shouldDraw {
// var paths = allPathsDict[currentPage] ?? []
// let currentPath = UIBezierPath()
// currentPath.move(to: convertedPoint)
// paths.append(currentPath)
// allPathsDict[currentPage] = paths
//
// let annotation = PDFAnnotation(bounds: currentPage.bounds(for: pdfView.displayBox), forType: .ink, withProperties: nil)
// annotation.color = UIColor.blue.withAlphaComponent(0.9)
// annotation.add(currentPath)
// currentPage.addAnnotation(annotation)
// }
// case .changed:
// if shouldDraw {
// if let paths = allPathsDict[currentPage], let currentPath = paths.last {
// currentPath.addLine(to: convertedPoint)
//
// currentPage.annotations.forEach { currentPage.removeAnnotation($0) }
//
// let updatedAnnotation = PDFAnnotation(bounds: currentPage.bounds(for: pdfView.displayBox), forType: .ink, withProperties: nil)
// updatedAnnotation.color = UIColor.blue.withAlphaComponent(0.9)
// paths.forEach { updatedAnnotation.add($0) }
// currentPage.addAnnotation(updatedAnnotation)
//
// // savePDFWithAnnotations()
//
// }
// }
//
//
// default:
// shouldDraw = false
// break
// }
// }
// }
// }
@objc func handlePanGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
let location = gestureRecognizer.location(in: gestureRecognizer.view)
if let pdfView = gestureRecognizer.view as? PDFView {
if let currentPage = pdfView.page(for: location, nearest: true) {
let convertedPoint = pdfView.convert(location, to: currentPage)
switch gestureRecognizer.state {
case .began:
shouldDraw = isPointWithinDrawingArea(convertedPoint, in: currentPage, of: pdfView)
if shouldDraw {
// Create a new path for the current annotation
var paths = allPathsDict[currentPage] ?? []
let currentPath = UIBezierPath()
currentPath.move(to: convertedPoint)
paths.append(currentPath)
allPathsDict[currentPage] = paths
// Add a new annotation
let annotation = PDFAnnotation(bounds: currentPage.bounds(for: pdfView.displayBox), forType: .ink, withProperties: nil)
annotation.color = UIColor.blue.withAlphaComponent(0.9)
annotation.add(currentPath)
currentPage.addAnnotation(annotation)
}
case .changed:
if shouldDraw {
if let paths = allPathsDict[currentPage], let currentPath = paths.last {
currentPath.addLine(to: convertedPoint)
currentPage.annotations.forEach { currentPage.removeAnnotation($0) }
let updatedAnnotation = PDFAnnotation(bounds: currentPage.bounds(for: pdfView.displayBox), forType: .ink, withProperties: nil)
updatedAnnotation.color = UIColor.blue.withAlphaComponent(0.9)
paths.forEach { updatedAnnotation.add($0) }
currentPage.addAnnotation(updatedAnnotation)
}
}
default:
shouldDraw = false
break
}
}
}
}
func isPointWithinDrawingArea(_ point: CGPoint, in page: PDFPage, of pdfView: PDFView) -> Bool {
let drawingArea = CGRect(x: 0, y: 0, width: pdfView.bounds.width, height: pdfView.bounds.height * 0.9)
return drawingArea.contains(point)
}
func clearDrawing() {
guard let pdfView = self.pdfView, let currentPage = pdfView.currentPage else { return }
let paths = allPathsDict[currentPage] ?? []
paths.forEach { path in
let annotation = PDFAnnotation(bounds: currentPage.bounds(for: pdfView.displayBox), forType: .ink, withProperties: nil)
annotation.color = UIColor.blue.withAlphaComponent(0.9)
annotation.add(path)
currentPage.addAnnotation(annotation)
}
allPathsDict[currentPage]?.removeAll()
}
func savePDFWithAnnotations() -> String? {
guard let pdfView = self.pdfView else {
return nil
}
let newPdfDocument = PDFDocument()
for i in 0..<(pdfView.document!.pageCount ?? 0) {
if let originalPage = pdfView.document?.page(at: i) {
let newPage = PDFPage(image: originalPage.thumbnail(of: CGSize(width: originalPage.bounds(for: pdfView.displayBox).width, height: originalPage.bounds(for: pdfView.displayBox).height), for: pdfView.displayBox))!
if let paths = allPathsDict[originalPage], paths.count > 0 {
let annotation = PDFAnnotation(bounds: originalPage.bounds(for: pdfView.displayBox), forType: .ink, withProperties: nil)
annotation.color = UIColor.blue.withAlphaComponent(0.9)
paths.forEach { annotation.add($0) }
newPage.addAnnotation(annotation)
}
newPdfDocument.insert(newPage, at: i)
}
}
// Save the new PDF document with annotations
// if let data = newPdfDocument.dataRepresentation() {
// let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// let pdfURL = documentsDirectory.appendingPathComponent("annotatedPDF.pdf")
//
// do {
// try data.write(to: pdfURL)
// print("PDF with annotations saved at: \(pdfURL)")
// } catch {
// print("Error saving PDF with annotations: \(error.localizedDescription)")
// }
// }
// saving the pdf as base64 string
if let data = newPdfDocument.dataRepresentation() {
let base64String = data.base64EncodedString()
return base64String
}
return nil
}
func clearLastAnnotation() {
guard let pdfView = self.pdfView else { return }
if let currentPage = pdfView.currentPage,
var paths = allPathsDict[currentPage],
let lastPath = paths.popLast() {
allPathsDict[currentPage] = paths
currentPage.annotations.forEach { currentPage.removeAnnotation($0) }
let updatedAnnotation = PDFAnnotation(bounds: currentPage.bounds(for: pdfView.displayBox), forType: .ink, withProperties: nil)
updatedAnnotation.color = UIColor.blue.withAlphaComponent(0.9)
paths.forEach { updatedAnnotation.add($0) }
currentPage.addAnnotation(updatedAnnotation)
}
}
func removeAllAnnotations() {
guard let pdfView = self.pdfView else { return }
for index in 0..<(pdfView.document?.pageCount ?? 0) {
if let page = pdfView.document?.page(at: index) {
let existingAnnotations = page.annotations
existingAnnotations.forEach { page.removeAnnotation($0) }
allPathsDict[page]?.removeAll()
}
}
if let currentPage = pdfView.currentPage {
allPathsDict[currentPage]?.removeAll()
}
}
func removeAnnotationsOnCurrentPage() {
guard let pdfView = self.pdfView, let currentPage = pdfView.currentPage else { return }
currentPage.annotations.forEach { annotation in
currentPage.removeAnnotation(annotation)
}
allPathsDict[currentPage] = []
}
}
Clear Annotations: In your clearDrawing() method, you are adding the cleared annotations back to the currentPage. Instead, you should remove the annotations from the currentPage and clear the corresponding paths in allPathsDict. Modify your clearDrawing() method as follows:
Ensure Proper Initialization: Make sure that you are initializing allPathsDict and clearing it properly when needed. In your Coordinator's initialization, you can clear all paths: