How to open files imported by UIDocumentPicker? - ios

In my app I picked files by UIDocumentPicker and put file names on a tableView. When clicking on a cell, I want the app open the file. I have no idea how to open the files picked before. Please help.
import UIKit
extension ViewController: UIDocumentMenuDelegate {
func documentMenu(documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
documentPicker.delegate = self
self.presentViewController(documentPicker, animated: true, completion: nil)
}
}
extension ViewController: UIDocumentPickerDelegate {
func documentPicker(controller: UIDocumentPickerViewController, didPickDocumentAtURL url: NSURL) {
if controller.documentPickerMode == UIDocumentPickerMode.Import {
dispatch_async(dispatch_get_main_queue()) {
if let fileName = url.lastPathComponent {
self.files.append(fileName)
}
}
}
}
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var files = [AnyObject]()
#IBOutlet weak var fileTableView: UITableView!
#IBAction func addDocuments(sender: AnyObject) {
let importMenu = UIDocumentMenuViewController(documentTypes: ["public.data", "public.text"], inMode: .Import)
importMenu.delegate = self
self.presentViewController(importMenu, animated: true, completion: nil)
let documentPicker = UIDocumentPickerViewController(documentTypes: ["public.data", "public.text"], inMode: .Import)
documentPicker.delegate = self
documentPicker.modalPresentationStyle = UIModalPresentationStyle.FullScreen
self.presentViewController(documentPicker, animated: true, completion: nil)
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}

You need to keep a reference to the entire URL, not just the filename.
Add the full url to self.files. Then update your cellForRowAtIndexPath to show just the lastPathComponent of that URL.
Then in didSelectRowAtIndexPath you have the access to the full URL of the file.

Related

Using UIDocumentPickerViewController(documentTypes:) in swift

While using UIDocumentPickerViewConroller in my code to select an audio file in app, this error came out and I can't find (documentTypes: )at UIDocumentPickerViewController.
#IBAction func AddMusic(_ sender: UIButton) {
let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypeAudio as String], in: .import)
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = false
present(documentPicker, animated: true, completion: nil)
}
}
extension ViewController: UIDocumentPickerDelegate{
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]){
}
}
UIDocumentPickerViewController(documentTypes: [String], in: UIDocumentPickerMode) was deprecated in iOS 14.0
That Method is replaced by this UIDocumentPickerViewController(forOpeningContentTypes contentTypes: [UTType], asCopy: Bool) method
First, You need to import UniformTypeIdentifiers
import UniformTypeIdentifiers
So, You can use this as below
let supportedTypes: [UTType] = [UTType.audio]
let pickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes, asCopy: true)
pickerViewController.delegate = self
pickerViewController.allowsMultipleSelection = false
pickerViewController.shouldShowFileExtensions = true
self.present(pickerViewController, animated: true, completion: nil)

Why are the results of PDFKit .beginFindStrings nil?

Self-teaching novice here.
My end goal:
iOS/Mac app that loads a directory of PDFs, searches each for an array of strings, and lists which PDFs contain which strings where.
Problem in prototyping for only one PDF:
I receive a perplexing nil from loading a chosen PDF, running .beginFindStrings(["and", "the"], withOptions: .caseInsensitive) and waiting for the Notification .PDFDocumentDidEndFind to check [PDFSelection] .
That shouldn't be. Memory shows the PDF is loaded. Am I doing something wrong with threads? I think I've followed the async advice here: PDFKit background search
Code
import UIKit
import MobileCoreServices
import PDFKit
class ViewController: UIViewController, UIDocumentPickerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
var matchesFound: PDFSelection?
#IBOutlet weak var resultsLabel: UILabel!
#IBAction func importPDF(_ sender: Any) {
let picker = UIDocumentPickerViewController(documentTypes: [kUTTypePDF as String], in: .import)
picker.delegate = self
picker.allowsMultipleSelection = false
self.present(picker, animated: true)
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard urls.count == 1 else {return}
let data = try! Data(contentsOf: urls[0])
let subjectPDF = PDFDocument.init(data: data)
guard subjectPDF!.isLocked == false else {return}
subjectPDF!.beginFindStrings(["the", "and"], withOptions: .caseInsensitive)
NotificationCenter.default.addObserver(self, selector: #selector(onDidFindMatch(_:)), name: Notification.Name.PDFDocumentDidEndFind, object: nil)
}
#objc func onDidFindMatch(_ notification: Notification) {
resultsLabel.text = "\(String(describing: matchesFound?.string))"
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
dismiss(animated: true, completion: nil)
}
}
Code with Markup
import UIKit
import MobileCoreServices
import PDFKit
class ViewController: UIViewController, UIDocumentPickerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
//Array of PDFSelection search results
var matchesFound: PDFSelection?
//Temporary display for search result strings
#IBOutlet weak var resultsLabel: UILabel!
//Choose a PDF to import, temporarily limited to one
#IBAction func importPDF(_ sender: Any) {
let picker = UIDocumentPickerViewController(documentTypes: [kUTTypePDF as String], in: .import)
picker.delegate = self
picker.allowsMultipleSelection = false
self.present(picker, animated: true)
}
//Load the picked PDF as subjectPDF, if unlocked
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard urls.count == 1 else {return}
let data = try! Data(contentsOf: urls[0])
let subjectPDF = PDFDocument.init(data: data)
guard subjectPDF!.isLocked == false else {return}
//Find temporary array of strings
subjectPDF!.beginFindStrings(["the", "and"], withOptions: .caseInsensitive)
//Trigger results readout upon search competion
NotificationCenter.default.addObserver(self, selector: #selector(onDidFindMatch(_:)), name: Notification.Name.PDFDocumentDidEndFind, object: nil)
}
//Readout found strings to temporary label
#objc func onDidFindMatch(_ notification: Notification) {
resultsLabel.text = "\(String(describing: matchesFound?.string))"
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
dismiss(animated: true, completion: nil)
}
}
The question was asked 7 months ago though, I hope you already found the solution.
Anyway the solution for your problem:
1- Move the line below to the viewDidLoad(), because you are adding an observer after triggering the beginFindString() method.
NotificationCenter.default.addObserver(self, selector: #selector(onDidFindMatch(_:)), name: Notification.Name.PDFDocumentDidEndFind, object: nil)
2- You are never assigning any value to matchesFound variable, so it's always nil.
3- To get the matches from beginFindString method, you need to add an observer for PDFDocumentDidFindMatch and get the data from userInfo instead of PDFDocumentDidEndFind.
PDFDocumentDidEndFind observer will be called when searching has been finished, you can use this observer for removing you loading view for instance.
Here is a sample code of the correct implementation:
var matchesFound = [PDFSelection]()
// MARK: Life cycle
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(didFindMatch(_:)), name: NSNotification.Name.PDFDocumentDidFindMatch, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// This method will get called every-time a match has been found.
#objc private func didFindMatch(_ sender: Notification) {
guard let selection = sender.userInfo?["PDFDocumentFoundSelection"] as? PDFSelection else { return }
self.matchesFound.append(selection)
}

iOS Swift, what is the url of the "on my iPhone" folder

I need to import to my app some document that have been saved into the "On My iPhone" folder. What would be the url of this folder? (those document (pdf) are saved outside the app by the user and I would like if possible to move/copy them to the app document folder but again I can't find that url.
Try this code (the way to pick needed document from iPhone Folder:
extension YourViewController: UIDocumentInteractionControllerDelegate {
/// If presenting a top a navigation stack, provide the navigation controller in order to animate in a manner consistent with the rest of the platform
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self.navigationController ?? self
}
}
extension YourViewController : UIDocumentPickerDelegate {
func initDocs() {
let pickerController = UIDocumentPickerViewController(documentTypes: ["public.item"], in: .import)
pickerController.delegate = self
if #available(iOS 11.0, *) {
pickerController.allowsMultipleSelection = false
}
present(pickerController, animated: false, completion: nil)
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
if let url = urls.first {
self.handleDoc(url: url) // method in your view controller
}
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
print("url = \(url)")
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
dismiss(animated: true, completion: nil)
}
}
In YourViewController call function:
#IBAction func importPem(_ sender: UIButton) {
initDocs()
UIDocumentInteractionController().presentPreview(animated: true)
}

How to open Safari View Controller from a Webview (swift)

I have an app that is currently using a webview and when certain links are clicked in the webview, it opens those links in Safari. I now want to implement the Safari View Controller(SVC) instead of booting it to the Safari app. I have done research and looked at examples on the SVC; however, all I see are ones that open the SVC from the click of a button. Does anyone have any suggestions for me to look at or to try?
Here is some of my code:
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == UIWebViewNavigationType.LinkClicked {
let host = request.URL!.host!;
if (host != "www.example.com"){
return true
} else {
UIApplication.sharedApplication().openURL(request.URL!)
return false
}
return true
}
func showLinksClicked() {
let safariVC = SFSafariViewController(URL: NSURL(string: "www.example.com")!)
self.presentViewController(safariVC, animated: true, completion: nil)
safariVC.delegate = self }
func safariViewControllerDidFinish(controller: SFSafariViewController) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
If I am understanding correctly you are loading a page on webview which has certain links now when user clicks on link you want to open those page in SVC. You can detect link click in webview using following delegate method and then open SVC from there.
EDIT
Based on edited question I can see that you are not calling showLinksClicked func , you can call this function as I have updated in following code and it should work.
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == UIWebViewNavigationType.LinkClicked {
self.showLinksClicked()
return false
}
return true;
}
func showLinksClicked() {
let safariVC = SFSafariViewController(url: URL(string: "www.google.com")!)
present(safariVC, animated: true, completion: nil)
safariVC.delegate = self
}
func safariViewControllerDidFinish(controller: SFSafariViewController) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
For Swift 3:
First, import SafariServices and integrate the delegate into your class:
import SafariServices
class YourViewController: SFSafariViewControllerDelegate {
Then, to open Safari with the specified url:
let url = URL(string: "http://www,google.com")!
let controller = SFSafariViewController(url: url)
self.present(controller, animated: true, completion: nil)
controller.delegate = self
And now you can implement the delegate callback to dismiss safari when the user is finished:
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
controller.dismiss(animated: true, completion: nil)
}
This piece of code will allow you to do this.
let safariVC = SFSafariViewController(URL: NSURL(string: "https://www.google.co.uk")!)
self.presentViewController(safariVC, animated: true, completion: nil)
safariVC.delegate = self
You may need to add this to the top of the class as well:
import SafariServices
Solution For Swift 4
Step 1:
import Safari Service In you Class
import SafariServices
Step 2:
Import SFSafariViewControllerDelegate in With your View Controller
class ViewController: UIViewController,SFSafariViewControllerDelegate {...}
Step 3:
Create A function to Open Safari View Controller.
func openSafariVC() {
let safariVC = SFSafariViewController(url: NSURL(string: "https://www.google.com")! as URL)
self.present(safariVC, animated: true, completion: nil)
safariVC.delegate = self
}
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
controller.dismiss(animated: true, completion: nil)
}
Step 4:
call the function openSafariVC
openSafariVC()
Note: Don't forget To Add Navigation Controller with your View
Controller.
Now your SafariVC is ready to open your Link within an app without Using UIWebView Oe WKWebView
Follow the steps :
On your controller file(e.g. ViewController.swift) import SafarriServices.
import SafariServices
Then where you want to open link write
let controller = SFSafariViewController(URL: NSURL(string: "https://www.google.co.uk")!)
self.presentViewController(controller, animated: true, completion: nil)
controller = self
Swift 4.2
This is how you can open-up Safari browser within your application.
import SafariServices
whenever you want to open, like wise on
#IBAction func btnOpenWebTapped(_ sender: UIButton) {
self.openWeb(contentLink: "https://www.google.com")
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.openWeb(contentLink: "https://www.google.com")
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.openWeb(contentLink: "https://www.google.com")
}
by write the custom function be like and you can customise the properties of the SFSafariViewController, preferredBarTintColor, preferredControlTintColor, dismissButtonStyle, barCollapsingEnabled
func openWeb(contentLink : String){
let url = URL(string: contentLink)!
let controller = SFSafariViewController(url: url)
controller.preferredBarTintColor = UIColor.darkGray
controller.preferredControlTintColor = UIColor.groupTableViewBackground
controller.dismissButtonStyle = .close
controller.configuration.barCollapsingEnabled = true
self.present(controller, animated: true, completion: nil)
controller.delegate = self
}
last and the most important thing is don't forget to bind the delegate of the SFSafariViewController with your view controller. you can do this by below mentioned extension code.
extension YourViewController: SFSafariViewControllerDelegate
{
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
controller.dismiss(animated: true, completion: nil)
}
}
Happy coding Thank you :)
import SafariServices
class ViewController: UIViewController, SFSafariViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
let url = URL(string: "http://www.google.com")!
let controller = SFSafariViewController(url: url)
self.present(controller, animated: true, completion: nil)
controller.delegate = self
}
}
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
dismiss(animated: true)
}
}

Why UIDocumentMenu Delegate to self doesn't work?

I am following Apple documentation for UIDocumentMenuViewController and the following is my code. importMenu.delegate = self doesn't work and Xcode shows: Cannot assign value of type 'ViewController' to type 'UIDocumentMenuDelegate?' . Please help. Thanks!
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let importMenu = UIDocumentMenuViewController(documentTypes: ["public.text", "public.data"], inMode: .Import)
importMenu.delegate = self
self.presentViewController(importMenu, animated: true, completion: nil)
}
}
According to UIDocumentMenuDelegate Protocol Reference, your ViewController must conforms to UIDocumentMenuDelegate and must implements documentMenu:didPickDocumentPicker:
extension ViewController: UIDocumentMenuDelegate {
func documentMenu(documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
// do stuffs here
}
}
your delegation class should extend from UIDocumentMenuViewDelegate in the view controller
import UIKit
class ViewController: UIViewController, UIDocumentMenuViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let importMenu = UIDocumentMenuViewController(documentTypes: ["public.text", "public.data"], inMode: .Import)
importMenu.delegate = self
self.presentViewController(importMenu, animated: true, completion: nil)
}
}

Resources