iosswiftpdfpdfkitios-pdfkit

Unable to save pdf after signature


I am able to sign a pdf, But when I tried to saved it, Its not saving the updated pdf file.

Code

override func viewDidAppear(_ animated: Bool) {
    pdfContainerView.usePageViewController(true, withViewOptions: nil)
    
    guard let signatureImage = signatureImage, let page = pdfContainerView.currentPage else { return }
    let pageBounds = page.bounds(for: .cropBox)
    let imageBounds = CGRect(x: pageBounds.midX, y: pageBounds.midY, width: 200, height: 100)
    let imageStamp = ImageStampAnnotation(with: signatureImage, forBounds: imageBounds, withProperties: nil)
    page.addAnnotation(imageStamp)
    btnSaveSign.isHidden = false

}
func setupPdfView(url:String) {
         if let documentURL = URL(string: url),
        
        let data = try? Data(contentsOf: documentURL),
        let document = PDFDocument(data: data) {
        
        // Set document to the view, center it, and set background color
        //pdfContainerView.displayMode = .singlePageContinuous
        pdfContainerView.document = document
        pdfContainerView.autoScales = true
        pdfContainerView.displayDirection = .horizontal
        pdfContainerView.backgroundColor = UIColor.white
        btnNewSign.isHidden = false
        lblPlaceholder.isHidden = true
        let panAnnotationGesture = UIPanGestureRecognizer(target: self, action: #selector(didPanAnnotation(sender:)))
        pdfContainerView.addGestureRecognizer(panAnnotationGesture)
        
    }
}

@objc func didPanAnnotation(sender: UIPanGestureRecognizer) {
    let touchLocation = sender.location(in: pdfContainerView)
    
    guard let page = pdfContainerView.page(for: touchLocation, nearest: true)
        else {
            return
    }
    let locationOnPage = pdfContainerView.convert(touchLocation, to: page)
    
    switch sender.state {
    case .began:
        
        guard let annotation = page.annotation(at: locationOnPage) else {
            return
        }
        
        if annotation.isKind(of: ImageStampAnnotation.self) {
            currentlySelectedAnnotation = annotation
        }
        
    case .changed:
        
        guard let annotation = currentlySelectedAnnotation else {
            return
        }
        let initialBounds = annotation.bounds
        // Set the center of the annotation to the spot of our finger
        annotation.bounds = CGRect(x: locationOnPage.x - (initialBounds.width / 2), y: locationOnPage.y - (initialBounds.height / 2), width: initialBounds.width, height: initialBounds.height)
        
        
        //print("move to \(locationOnPage)")
    case .ended, .cancelled, .failed:
        currentlySelectedAnnotation = nil
    default:
        break
    }
}
   

@objc func clickButton(){
    let importMenu = UIDocumentPickerViewController(documentTypes: [String(kUTTypePDF)], in: .import)
    importMenu.delegate = self
    importMenu.modalPresentationStyle = .formSheet
    self.present(importMenu, animated: true, completion: nil)
}



public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    guard let myURL = urls.first else {
        return
    }
    print("import result : \(myURL)")
    self.pdfURLToSave = "\(myURL)"
    let pdfView = PDFView(frame: view.frame)
    
    title = "My PDF Viewer"
    setupPdfView(url:"\(myURL)")
    
}

func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
    return self//or use return self.navigationController for fetching app navigation bar colour
}


public func documentMenu(_ documentMenu:UIDocumentPickerViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
    documentPicker.delegate = self
    present(documentPicker, animated: true, completion: nil)
}

Code snippet for save file-

    @IBAction func btnSaveAction(_ sender: Any) {
            if let documentURL = URL(string: self.pdfURLToSave),
       
       let data = try? Data(contentsOf: documentURL),
       let document = PDFDocument(data: data) {
        
        if let data = document.dataRepresentation(){
            
            let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
            let documentsDirectory = paths[0] // Get documents folder
            let folderPathUrl = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("SignApp.pdf")
            if FileManager.default.fileExists(atPath: folderPathUrl.path){
                try? FileManager.default.removeItem(at: folderPathUrl)
                
            }
            pdfContainerView.document?.write(toFile: "\(folderPathUrl)")
            
        }
    }
    
        }

ImageStampAnnotation Class

class ImageStampAnnotation: PDFAnnotation {
    
    var image: UIImage!
    
    // A custom init that sets the type to Stamp on default and assigns our Image variable
    init(with image: UIImage!, forBounds bounds: CGRect, withProperties properties: [AnyHashable : Any]?) {
        super.init(bounds: bounds, forType: PDFAnnotationSubtype.stamp, withProperties: properties)
        
        self.image = image
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    override func draw(with box: PDFDisplayBox, in context: CGContext) {
        
        // Get the CGImage of our image
        guard let cgImage = self.image.cgImage else { return }
        
        // Draw our CGImage in the context of our PDFAnnotation bounds
        context.draw(cgImage, in: self.bounds)
        
    }
}

Error-

CGPDFContextCreate: failed to create PDF context delegate..

App screen shots-

enter image description here enter image description here enter image description here


Solution

  • It's not possible for me to edit your code directly to show a proper solution, since your code isn't complete, but here is a short coordinator class, including your pan annotation code that saves an updated file each time a pan gesture is completed:

    class Coordinator : NSObject {
        var pdfView : PDFView?
        var currentlySelectedAnnotation : PDFAnnotation?
        
        func getDocumentsDirectory() -> URL {
            let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
            let documentsDirectory = paths[0]
            return documentsDirectory
        }
        
        func save() {
            guard let pdfView = pdfView else {
                return
            }
            let url = getDocumentsDirectory().appendingPathComponent("SavedPDF.pdf")
            pdfView.document?.write(to: url)
            print("Wrote pdf to: ", url)
        }
        
        @objc func didPanAnnotation(sender: UIPanGestureRecognizer) {
            guard let pdfContainerView = pdfView else {
                fatalError()
            }
            
            let touchLocation = sender.location(in: pdfContainerView)
            
            guard let page = pdfContainerView.page(for: touchLocation, nearest: true)
            else {
                return
            }
            let locationOnPage = pdfContainerView.convert(touchLocation, to: page)
            
            switch sender.state {
            case .began:
                
                guard let annotation = page.annotation(at: locationOnPage) else {
                    return
                }
                
                print("Set")
                
                if annotation.isKind(of: ImageStampAnnotation.self) {
                    currentlySelectedAnnotation = annotation
                }
                
            case .changed:
                
                guard let annotation = currentlySelectedAnnotation else {
                    return
                }
                let initialBounds = annotation.bounds
                // Set the center of the annotation to the spot of our finger
                annotation.bounds = CGRect(x: locationOnPage.x - (initialBounds.width / 2), y: locationOnPage.y - (initialBounds.height / 2), width: initialBounds.width, height: initialBounds.height)
                
                
                //print("move to \(locationOnPage)")
            case .ended, .cancelled, .failed:
                currentlySelectedAnnotation = nil
                save()
            default:
                break
            }
        }
    }
    

    For context, if it helps, this is how I set up the view controller and the PDFView initially:

    let vc = UIViewController()
    let pdfContainerView = PDFView()
    let fileUrl = Bundle.main.url(forResource: "High Noon - SCORE", withExtension: "pdf")!
    let pdfDocument = PDFDocument(url: fileUrl)
    pdfContainerView.document = pdfDocument
    pdfContainerView.autoScales = true
    vc.view.addSubview(pdfContainerView)
    let panAnnotationGesture = UIPanGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.didPanAnnotation(sender:)))
    pdfContainerView.addGestureRecognizer(panAnnotationGesture)
    let page = pdfContainerView.currentPage!
    let pageBounds = page.bounds(for: .cropBox)
    let imageBounds = CGRect(x: pageBounds.midX, y: pageBounds.midY, width: 200, height: 100)
    let imageStamp = ImageStampAnnotation(with: UIImage(systemName: "pencil"), forBounds: imageBounds, withProperties: nil)
    page.addAnnotation(imageStamp)