QLPreviewController Overlay - ios

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?

Related

QuickLook/QLPreviewController PDF Text Annotation background becomes black and font becomes small

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
}
}

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

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

Hide or disable share button from uidocumentinteractioncontroller in swift 5

In my application, I'm using the QuickLook framework to view the document files such as pdf, ppt, doc, etc. etc. But due to privacy concerns, I don't want that the user can share this document with others so please let me know how to disable/hide the share button and also the copy-paste option.
I know this question can be asked by a number of times and tried many solutions but nothing works for me
hide share button from QLPreviewController
UIDocumentInteractionController remove Actions Menu
How to hide share button in QLPreviewController using swift?
Hide right button n QLPreviewController?
Please suggest to me to achieve this.
Here is my demo code:
import UIKit
import QuickLook
class ViewController: UIViewController {
lazy var previewItem = NSURL()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func displayLocalFile(_ sender: UIButton){
let previewController = QLPreviewController()
// Set the preview item to display
self.previewItem = self.getPreviewItem(withName: "samplePDf.pdf")
previewController.dataSource = self
self.present(previewController, animated: true, completion: nil)
}
#IBAction func displayFileFromUrl(_ sender: UIButton){
// Download file
self.downloadfile(completion: {(success, fileLocationURL) in
if success {
// Set the preview item to display======
self.previewItem = fileLocationURL! as NSURL
// Display file
let previewController = QLPreviewController()
previewController.dataSource = self
self.present(previewController, animated: true, completion: nil)
}else{
debugPrint("File can't be downloaded")
}
})
}
func getPreviewItem(withName name: String) -> NSURL{
// Code to diplay file from the app bundle
let file = name.components(separatedBy: ".")
let path = Bundle.main.path(forResource: file.first!, ofType: file.last!)
let url = NSURL(fileURLWithPath: path!)
return url
}
func downloadfile(completion: #escaping (_ success: Bool,_ fileLocation: URL?) -> Void){
let itemUrl = URL(string: "https://images.apple.com/environment/pdf/Apple_Environmental_Responsibility_Report_2017.pdf")
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent("filename.pdf")
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
debugPrint("The file already exists at path")
completion(true, destinationUrl)
// if the file doesn't exist
} else {
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: itemUrl!, completionHandler: { (location, response, error) -> Void in
guard let tempLocation = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: tempLocation, to: destinationUrl)
print("File moved to documents folder")
completion(true, destinationUrl)
} catch let error as NSError {
print(error.localizedDescription)
completion(false, nil)
}
}).resume()
}
}
}
//MARK:- QLPreviewController Datasource
extension ViewController: QLPreviewControllerDataSource {
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
controller.navigationItem.rightBarButtonItem = nil
return self.previewItem as QLPreviewItem
}
}
Please provide your suggestion to do so or any other framework to view different file formats.
Here is the image
Find below adopted my approach to your code (with modifications to test locally, but the code should be clear). The idea is
a) to override, which is completely allowed by API, needed classes to intercept modification
b) to use intentionally own UINavigationController, as only one navigation controller can be in stack
So here is code:
// Custom navigation item that just blocks adding right items
class MyUINavigationItem: UINavigationItem {
override func setRightBarButtonItems(_ items: [UIBarButtonItem]?, animated: Bool) {
// forbidden to add anything to right
}
}
// custom preview controller that provides own navigation item
class MyQLPreviewController: QLPreviewController {
private let item = MyUINavigationItem(title: "")
override var navigationItem: UINavigationItem {
get { return item }
}
}
class MyViewController : UIViewController, QLPreviewControllerDataSource {
lazy var previewItem = NSURL()
override func loadView() {
let view = UIView()
view.backgroundColor = .white
// just stub testing code
let button = UIButton(type: .roundedRect)
button.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
button.setTitle("Show", for: .normal)
button.addTarget(self, action:
#selector(displayLocalFile(_:)), for: .touchDown)
view.addSubview(button)
self.view = view
}
#objc func displayLocalFile(_ sender: UIButton){
let previewController = MyQLPreviewController() // << custom preview
// now navigation item is fully customizable
previewController.navigationItem.title = "samplePDF.pdf"
previewController.navigationItem.leftBarButtonItem =
UIBarButtonItem(barButtonSystemItem: .done, target: self,
action: #selector(closePreview(_:)))
// wrap it into navigation controller
let navigationController = UINavigationController(rootViewController: previewController)
// Set the preview item to display
self.previewItem = self.getPreviewItem(withName: "samplePDF.pdf")
previewController.dataSource = self
// present navigation controller with preview
self.present(navigationController, animated: true, completion: nil)
}
#objc func closePreview(_ sender: Any?) {
self.dismiss(animated: true) // << dismiss preview
}
func getPreviewItem(withName name: String) -> NSURL{
// Code to diplay file from the app bundle
let file = name.components(separatedBy: ".")
let path = Bundle(for: type(of: self)).path(forResource: file.first!, ofType: file.last!)
let url = NSURL(fileURLWithPath: path!)
return url
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return self.previewItem as QLPreviewItem
}
}

Swift UIActivityViewController

Could anyone tell me how to implement "Open in Safari" in UIActivityViewController? I know this questions is a duplicate of another question posted a long time ago, and the method at that time was by using a framework that can no longer be used.
The data I am sharing is a URL. I already have a fully working ActivityVC and I only need to add that “open in safari” button.
Thank you very much.
code:
#IBAction func shareButtonPressed(_ sender: UIButton) {
let activityVC = UIActivityViewController(activityItems: [URL(string: urlStr)!], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = self.view
self.present(activityVC, animated: true, completion: nil)
}
You need to implement your own activity, please check the code below.
import UIKit
final class SafariActivity: UIActivity {
var url: URL?
override var activityImage: UIImage? {
return UIImage(named: "SafariActivity")!
}
override var activityTitle: String? {
return NSLocalizedString("Open in Safari", comment:"")
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
for item in activityItems {
if
let url = item as? URL,
UIApplication.shared.canOpenURL(url)
{
return true
}
}
return false
}
override func prepare(withActivityItems activityItems: [Any]) {
for item in activityItems {
if
let url = item as? URL,
UIApplication.shared.canOpenURL(url)
{
self.url = url
}
}
}
override func perform() {
var completed = false
if let url = self.url {
completed = UIApplication.shared.openURL(url)
}
activityDidFinish(completed)
}
}
let url = URL(string: "http://www.apple.com")!
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: [SafariActivity()])
present(activityViewController, animated: true, completion: nil)
Updated to Swift 5.1 & iOS 13
Bonus:
ActivityType extension to use with .excludedActivityTypes.
UIImage(systemName:) to use SF Symbols plus .applyingSymbolConfiguration to take advantage of its flexibility.
To improve:
Implement completion handler on UIApplication.shared.open to handle errors (unlikely to occur).
import UIKit
extension UIActivity.ActivityType {
static let openInSafari = UIActivity.ActivityType(rawValue: "openInSafari")
}
final class SafariActivity: UIActivity {
var url: URL?
var activityCategory: UIActivity.Category = .action
override var activityType: UIActivity.ActivityType {
.openInSafari
}
override var activityTitle: String? {
"Open in Safari"
}
override var activityImage: UIImage? {
UIImage(systemName: "safari")?.applyingSymbolConfiguration(.init(scale: .large))
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
activityItems.contains { $0 is URL ? UIApplication.shared.canOpenURL($0 as! URL) : false }
}
override func prepare(withActivityItems activityItems: [Any]) {
url = activityItems.first { $0 is URL ? UIApplication.shared.canOpenURL($0 as! URL) : false } as? URL
}
override func perform() {
if let url = url {
UIApplication.shared.open(url)
}
self.activityDidFinish(true)
}
}
Try this Link if it meets your requirement
Link - https://bjartes.wordpress.com/2015/02/19/creating-custom-share-actions-in-ios-with-swift/
Code Required
class FavoriteActivity: UIActivity {
override func activityType() -> String? {
return "TestActionss.Favorite"
}
override func activityTitle() -> String? {
return "Add to Favorites"
}
override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
NSLog("%#", __FUNCTION__)
return true
}
override func prepareWithActivityItems(activityItems: [AnyObject]) {
NSLog("%#", __FUNCTION__)
}
override func activityViewController() -> UIViewController? {
NSLog("%#", __FUNCTION__)
return nil
}
override func performActivity() {
// Todo: handle action:
NSLog("%#", __FUNCTION__)
self.activityDidFinish(true)
}
override func activityImage() -> UIImage? {
return UIImage(named: "favorites_action")
}
}
Usage
#IBAction func showAvc(sender: UIButton) {
let textToShare = "Look at this awesome website!"
let myWebsite = NSURL(string: "http://www.google.com/")!
let objectsToShare = [textToShare, myWebsite]
let applicationActivities = [FavoriteActivity()]
let avc = UIActivityViewController(activityItems: objectsToShare, applicationActivities: applicationActivities)
self.presentViewController(avc, animated: true, completion: nil)
}

How to pass a value from a delegate to another one in swift?

I want to pass the url in documentPicker to previewController. Since all of them are delegates, I can't simply return because that will violate the protocol. How to pass the data from a view to another view? Thanks!
func previewController(controller: QLPreviewController!, previewItemAtIndex index: Int) -> QLPreviewItem! {
//var doc = NSURL(fileURLWithPath: url)
return doc
}
func documentPicker(controller: UIDocumentPickerViewController, didPickDocumentAtURL url: NSURL) {
println("\(url)")
var quickView = QLPreviewController()
quickView.dataSource = self
presentViewController(quickView, animated: true, completion: nil)
}
~~~~~~~~~~~Update~~~~~~~~~6.2~~~~~~~~~~~~~~
Now I follow the idea of the answer as follow
func documentPicker(controller: UIDocumentPickerViewController, didPickDocumentAtURL url: NSURL) {
println("\(url)")
var a: String = "\(url)"
NSUserDefaults.standardUserDefaults().setObject(a, forKey: "URL")
var quickView = QLPreviewController()
quickView.dataSource = self
presentViewController(quickView, animated: true, completion: nil)
}
and
func previewController(controller: QLPreviewController!, previewItemAtIndex index: Int) -> QLPreviewItem!{
var url = NSUserDefaults.standardUserDefaults().objectForKey("URL") as! String
var doc = NSURL(fileURLWithPath: url)
return doc
}
By doing so I avoid the format issue of NSUserDefaults, but the program still break at doc (Thread 1: breakpoint 1.1)
For example, when run it and select a file, the url is
file:///private/var/mobile/Containers/Data/Application/DEE5190D-6E3F-4400-8866-4668B830C588/tmp/DocumentPickerIncoming/Experiment_7.PDF
and the doc is : Optional(file:/private/var/mobile/Containers/Data/Application/DEE5190D-6E3F-4400-8866-4668B830C588/tmp/DocumentPickerIncoming/Experiment_7.PDF -- file:///)
they are the same. Why the app break?
Well you can store your Url in NSUserDefaults. And use it whenever you want. You can store text in NSUserDefaults
func documentPicker(controller: UIDocumentPickerViewController, didPickDocumentAtURL url: NSURL) {
println("\(url)")
NSUserDefaults.standardUserDefaults().setObject(url, forKey: "URL")
var quickView = QLPreviewController()
quickView.dataSource = self
presentViewController(quickView, animated: true, completion: nil)
}
and retrieve it in
func previewController(controller: QLPreviewController!, previewItemAtIndex index: Int) -> QLPreviewItem! {
var doc = NSUserDefaults.standardUserDefaults().objectForKey("URL") as! NSUrl
return doc
}

Resources