IOS Pdf kit ink annotations reappear once cleared. Once cleared old annotations should not appear

44 views Asked by At

I have three buttons in my swiftui view..

  1. clear(clear current page);
  2. clear all;
  3. 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] = []
        }
    


}
1

There are 1 answers

1
Kemal On

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:

func clearDrawing() {
    guard let pdfView = self.pdfView, let currentPage = pdfView.currentPage else { return }

    currentPage.annotations.forEach { annotation in
        currentPage.removeAnnotation(annotation)
    }

    allPathsDict[currentPage]?.removeAll()
}

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:

init(parent: PDFViewContainerMulti) {
    self.parent = parent
    self.pdfView = parent.pdfView
    // Clear all paths when the Coordinator is initialized
    self.allPathsDict.removeAll()
}