Can we add text, shape and signature in Photo Markup with Pencil Kit?

I figured it out, finally! It’s the QLPreviewController!

Editing with shapes, arrows and signature is only available for iOS13+.
First of all we need to read the file from an url, so set it up with init. I go with something like this as base and append the filename, also with file extension, e.g. .pdf:

FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!

You can’t save it in the tempDirectory, because the QLPreviewViewController needs access. No permission could result in an error like this:

AX Lookup problem - errorCode:1100 error:Permission denied portName:

Your customVC should look like this:

import UIKit
import QuickLook
class CustomVC: UIViewController {
    var url: URL    

    init(url: URL) {
        self.url = url
    }

....func viewDidLoad() and stuff ......

    func editFile() {
        let editor = QLPreviewController()
        editor.dataSource = self
        editor.delegate = self
        editor.setEditing(true, animated: true)
        present(editor, animated: true, completion: nil)
    }
}

// Load the file in the QLPreviewController with DataSource
extension CustomVC: QLPreviewControllerDataSource {
    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
        return 1
    }
    
    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
        return self.url! as QLPreviewItem
    }  
}

// Make editing available with Delegate
@available(iOS 13.0, *)
extension CustomVC: QLPreviewControllerDelegate {
    
    func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode {
        return .updateContents
    }
    
    func previewController(_ controller: QLPreviewController, didUpdateContentsOf previewItem: QLPreviewItem) {
        print("UPDATE")
    }
    
    func previewController(_ controller: QLPreviewController, didSaveEditedCopyOf previewItem: QLPreviewItem, at modifiedContentsURL: URL) {
        print("SAVED at \(modifiedContentsURL)")
    }
}

The markup button will show automatically if you have implemented these functions in the delegate correctly.

You can also add more barButtonItems for this VC like normal with an extra navigationController, e.g. something like this in the editFile function:

        let navController = UINavigationController(rootViewController: editor)
        let customButton = UIBarButtonItem(image: UIImage(systemName: "yourImageName"), style: .plain, target: self, action: #selector(customButtonTapped(_:)))
        let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonTapped(_:)))

        if var items = editor.navigationItem.rightBarButtonItems {
            items.append(customButton)
            editor.navigationItem.rightBarButtonItems = items
        } else {
            editor.navigationItem.rightBarButtonItems = [customButton]
        }
        editor.navigationItem.leftBarButtonItem = doneButton
        viewController?.present(navController, animated: true, completion: nil)
        self.navigationController = navController

Leave a Comment