I am Using QLPreviewController for editing the PDF. Everything is working properly but
if I add text annotation from the QLPreviewController and click on the done button it is working as expected I am getting the URL with edited PDF and if I do other changes for example add image annotation from outside of QLPreviewController and save that pdf using the code
self.pdfDocument.write(to: self.pdfUrl)
and if I open that pdf in the QLPReviewController and tap on the previously added text then the text annotation background becomes Black and the font becomes small.
I have attached the video please check.
**Video Link : ** https://www.dropbox.com/s/m0ql1csr8pars3t/Issue_video.mov?dl=0
I have used the below code for pdf mark up
func openPreview(_ url : URL){
let previewController = QLPreviewController()
previewController.isEditing = true
previewController.delegate = self
self.pdfUrl = url
previewController.dataSource = self
previewController.modalPresentationStyle = .overFullScreen
self.present(previewController, animated: true, completion: nil)
}
extension ViewController : QLPreviewControllerDataSource,QLPreviewControllerDelegate{
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return self.pdfUrl as QLPreviewItem
}
func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode {
return .updateContents
}
func previewControllerWillDismiss(_ controller: QLPreviewController) {
}
func previewController(_ controller: QLPreviewController, didUpdateContentsOf previewItem: QLPreviewItem) {
DispatchQueue.main.async {
self.pdfUrl = previewItem.previewItemURL
self.setPDFFile()
self.pdfDocument.write(to: self.pdfUrl)
}
}
}
func setPDFFile(){
if let doc = PDFDocument(url: self.pdfUrl){
self.pdfDocument = doc
self.pdfView.document = doc
}
}
Related
I am not getting any option to add text, shape and signature while markup a photo with PencelKit in my app. This option is available in Apple's Photos App. I have tried to access this with various properties of CanvasView and PKToolPicker, but with no success.
self.canvasView?.drawing = PKDrawing()
self.canvasView.allowsFingerDrawing = true
if let window = self.view.window, let toolPicker = PKToolPicker.shared(for: window) {
toolPicker.setVisible(true, forFirstResponder: self.canvasView)
toolPicker.addObserver(self.canvasView)
self.canvasView.becomeFirstResponder()
}
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
I'm showing a local pdf file in Quicklook by presenting the QLPreviewController to the view controller
func showFileWithPath(path: String){
let quickLookController = QLPreviewController()
quickLookController.dataSource = self
quickLookController.delegate = self
self.present(quickLookController, animated: true, completion: nil)
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
let pdfURL = Bundle.main.url(forResource: "Mobile-Application", withExtension: "pdf", subdirectory: nil, localization: nil)
return pdfURL! as QLPreviewItem
}
In iOS 10, quicklook works fine. But in ios 11 the toolbar is hidden and the toolbar buttons overlaps each other in the top left. It might be due to the toolbar absence.
Why is this happening? is there anyone seeing this issue in ios 11?
This question already has answers here:
How to display remote document using QLPreviewController in swift
(2 answers)
Closed 5 years ago.
Currently I am using QuickLook module to open pdf from network, but it shows a blank page with error "Couldn't issue file extension for url: https://testing-xamidea.s3.amazonaws.com/flowchart/20171103182728150973368.pdf" in console. I guess QuickLook can only open locally saved Pdf files. Is is possible to load pdf from network using quicklook? . This is my code so far- {fileURL contains url from which pdf is to be loaded, also ive set the delegates etc)
extension FlowchartVC:QLPreviewControllerDelegate,QLPreviewControllerDataSource {
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
let url : NSURL! = NSURL(string : fileURL)
return url
}
func previewControllerWillDismiss(_ controller: QLPreviewController) {
self.dismiss(animated: true, completion: nil)
}
}
You need to save the file to disk first and then you can present the pdf. There is no way to present it with QuickLook if the file is in a remote location. The file is saved in the temporary directory. Here is an example view controller showing how it could be done.
Swift 5:
import UIKit
import QuickLook
class ViewController: UIViewController, QLPreviewControllerDataSource {
// Remote url pdf I found on google
let itemURL = URL(string: "https://www.ets.org/Media/Tests/GRE/pdf/gre_research_validity_data.pdf")!
var fileURL: URL?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let quickLookController = QLPreviewController()
quickLookController.dataSource = self
do {
// Download the pdf and get it as data
// This should probably be done in the background so we don't
// freeze the app. Done inline here for simplicity
let data = try Data(contentsOf: itemURL)
// Give the file a name and append it to the file path
fileURL = FileManager().temporaryDirectory.appendingPathComponent("sample.pdf")
if let fileUrl = fileURL {
// Write the pdf to disk in the temp directory
try data.write(to: fileUrl, options: .atomic)
}
// Make sure the file can be opened and then present the pdf
if QLPreviewController.canPreview(fileUrl as QLPreviewItem) {
quickLookController.currentPreviewItemIndex = 0
present(quickLookController, animated: true, completion: nil)
}
} catch {
// cant find the url resource
}
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return fileURL! as QLPreviewItem
}
}
Swift 3:
import UIKit
import QuickLook
class ViewController: UIViewController, QLPreviewControllerDataSource {
// Remote url pdf I found on google
let itemURL = URL(string: "https://www.ets.org/Media/Tests/GRE/pdf/gre_research_validity_data.pdf")!
var fileURL = URL(string: "")
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let quickLookController = QLPreviewController()
quickLookController.dataSource = self
do {
// Download the pdf and get it as data
// This should probably be done in the background so we don't
// freeze the app. Done inline here for simplicity
let data = Data(contentsOf: itemURL)
// Give the file a name and append it to the file path
fileURL = FileManager().temporaryDirectory.appendingPathComponent("sample.pdf")
// Write the pdf to disk
try data?.write(to: fileURL!, options: .atomic)
// Make sure the file can be opened and then present the pdf
if QLPreviewController.canPreview(fileUrl as QLPreviewItem) {
quickLookController.currentPreviewItemIndex = 0
present(quickLookController, animated: true, completion: nil)
}
} catch {
// cant find the url resource
}
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return fileURL! as QLPreviewItem
}
}
Here is the file showing in the simulator. Using a sample project with just that code.
I created a new view controller in storyboard. Connected it to the ListViewController. The ListViewController fills in the correct data to aboutDict[String:Any]. The fileURLs[] is built and func numberOfPreviewItems(in: QLPreviewController) -> Int is called - the debugger shows the fileURLs[] is as expected:
Printing description of self.fileURLs: ▿ 1 element
- 0 : file:///Users/kent/Library/Developer/CoreSimulator/Devices/5E23825C-DF99-455A-BEB1-F73398E7759F/data/Containers/Bundle/Application/307ED7DF-C07C-4C0A-BA78-938BABE7C22C/WINSystemInfo.app/ID-51A_E_PLUS2.pdf`
But func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem and func previewController(_ controller: QLPreviewController, shouldOpen url: URL, for item: QLPreviewItem) -> Bool are not called.
I get a nice ViewController that was pushed into the navigation stack with the correct name for the back button, and a gray page with centered text "No file to preview".
On pressing 'back' button, func previewControllerWillDismiss(_ controller: QLPreviewController) is called. So some of the delegate and datasource functions are being called.
I must be missing something simple...
I attached my class FileViewController.swift below:
class FileViewController: QLPreviewController, QLPreviewControllerDelegate, QLPreviewControllerDataSource {
var aboutDict = [String: Any]()
// QuickLook data
var fileURLs = [QLPreviewItem]()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
let filename = aboutDict["filename"] as! String?
let filetype = aboutDict["filetype"] as! String?
title = aboutDict["title"] as! String?
dataSource = self
delegate = self
if let fileUrl = Bundle.main.url(forResource: filename, withExtension: filetype, subdirectory: nil, localization: nil)
{
let filePreview = fileUrl as QLPreviewItem
fileURLs.append(filePreview)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Preview controller datasource functions
func numberOfPreviewItems(in: QLPreviewController) -> Int {
return fileURLs.count
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return fileURLs[index]
}
// MARK: - Preview controller delegate functions
func previewControllerWillDismiss(_ controller: QLPreviewController) {
debug("previewControllerWillDismiss")
}
func previewController(_ controller: QLPreviewController, shouldOpen url: URL, for item: QLPreviewItem) -> Bool {
return true
}
}
What am I missing?
Thank you.
I just had this problem and it was simply because I had forgotten to set my datasource on the QLPreviewController...
I'm creating a QLPreviewController:
func numberOfPreviewItemsInPreviewController(controller: QLPreviewController) -> Int {
return 1
}
func previewController(controller: QLPreviewController, previewItemAtIndex index: Int) -> QLPreviewItem {
let path = NSBundle.mainBundle().pathForResource("test", ofType: "pdf")
let url = NSURL.fileURLWithPath(path!)
return url
}
#IBAction func previewAction(sender: AnyObject) {
let preview = QLPreviewController()
preview.dataSource = self
self.presentViewController(preview, animated: true, completion: nil)
}
Now I want to add onto the QLPreviewController some kind of Overlay, at last a Label (that should always be seen).
Is there a way todo so?