How to use UIDocumentInteractionController? - ios

I have created a program that loads PDF files. I want when the user is editing that they can publish the file anywhere in PDF format.
Do I use UIDocumentInteractionController or use UIActivityViewController?
Here is the code:
import UIKit
import PDFKit
#available(iOS 11.0, *)
#available(iOS 11.0, *)
class PDFViewControllerEN: UIViewController {
var document: UIDocumentInteractionController!
override func viewDidLoad() {
super.viewDidLoad()
// retrieve URL to file in main bundle`
}
#IBOutlet var pdfview: UIView!
#IBAction func share(_ sender: UIButton) {
}
#IBAction func doAction2(_ sender: UIBarButtonItem) {
document.presentOptionsMenu(from: view.bounds, in: view, animated: true)
}
override func viewWillAppear(_ animated: Bool) {
//Here you are going to display your PdfController
//PDFController that is seprate class you had created to show pdf file being opened
//i.e
//check which button was being selected
switch ButtonSelected.Tag {
case 0:
var document: UIDocumentInteractionController = {
let pdfView = PDFView(frame: UIScreen.main.bounds)
let url = Bundle.main.url(forResource: "EN1", withExtension: "pdf")
let vc = UIDocumentInteractionController(url: url!)
pdfView.document = PDFDocument(url: url!)
view.addSubview(pdfView)
vc.delegate = self
return vc
}()
// document.presentPreview(animated: true)
break
case 1:
//here control when you selected button with tag 0
//here need to open pdf AR2
//set Frame here all bounds
var document: UIDocumentInteractionController = {
let pdfView = PDFView(frame: UIScreen.main.bounds)
let url = Bundle.main.url(forResource: "EN2", withExtension: "pdf")
let vc = UIDocumentInteractionController(url: url!)
pdfView.document = PDFDocument(url: url!)
view.addSubview(pdfView)
vc.delegate = self
return vc
}()
break
case 2:
//here control when you selected button with tag 0
//here need to open pdf AR2
//set Frame here all bounds
var document: UIDocumentInteractionController = {
let pdfView = PDFView(frame: UIScreen.main.bounds)
let url = Bundle.main.url(forResource: "EN3", withExtension: "pdf")
let vc = UIDocumentInteractionController(url: url!)
pdfView.document = PDFDocument(url: url!)
view.addSubview(pdfView)
vc.delegate = self
return vc
}()
break
default:
//Error Case
print("No tag Value Available")
}
}
}
#available(iOS 11.0, *)
extension PDFViewControllerEN: UIDocumentInteractionControllerDelegate {
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self
}
}

I have tried and explained in detail about UIDocumentInteractionController. Refer this link if you want to check the details.
https://medium.com/if-let-swift-programming/managing-files-in-ios-dfcdfdc1f426
Code
extension ViewController {
/// This function will set all the required properties, and then provide a preview for the document
func share(url: URL) {
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentPreview(animated: true)
}
/// This function will store your document to some temporary URL and then provide sharing, copying, printing, saving options to the user
func storeAndShare(withURLString: String) {
guard let url = URL(string: withURLString) else { return }
/// START YOUR ACTIVITY INDICATOR HERE
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let tmpURL = FileManager.default.temporaryDirectory
.appendingPathComponent(response?.suggestedFilename ?? "fileName.png")
do {
try data.write(to: tmpURL)
} catch {
print(error)
}
DispatchQueue.main.async {
/// STOP YOUR ACTIVITY INDICATOR HERE
self.share(url: tmpURL)
}
}.resume()
}
}
extension ViewController: UIDocumentInteractionControllerDelegate {
/// If presenting atop 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 {
guard let navVC = self.navigationController else {
return self
}
return navVC
}
}
extension URL {
var typeIdentifier: String? {
return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
}
var localizedName: String? {
return (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName
}
}
Calling
#IBAction func showOptionsTapped(_ sender: UIButton) {
/// Passing the remote URL of the file, to be stored and then opted with mutliple actions for the user to perform
storeAndShare(withURLString: "https://images5.alphacoders.com/581/581655.jpg")
}
Note
https://www.bignerdranch.com/blog/working-with-the-files-app-in-ios-11/
Before your files can appear in the Files app, you must indicate that your app supports Open in Place and File Sharing Enabled. These options are configured using keys in your Info.plist file.
The first key is UIFileSharingEnabled, which enables iTunes sharing of files in your Documents folder.
The second key is LSSupportsOpeningDocumentsInPlace, which grants the local file provider access to files in your Documents folder.
Add these keys to your Info.plist and set their values to YES.

Using UIDocumentInteractionController is quite easy. You just need to know the url of your file, then you present the menu:
/// Needs to be global, otherwise the controller will be destroyed when the file is handed over to target application
var documentInteractionController: UIDocumentInteractionController!
class MyViewController: UIViewController {
var url: URL
...
#IBAction func share(_ sender: UIBarButtonItem) {
documentInteractionController = UIDocumentInteractionController()
documentInteractionController.url = url
documentInteractionController.uti = url.uti
documentInteractionController.presentOptionsMenu(from: sender, animated: true)
}
}
extension URL {
var uti: String {
return (try? self.resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier ?? "public.data"
}
}

Silly case but might help to someone.
To anyone who can't save files check if your controller is alive. My problem was that my UIDocumentInteractionController was destroyed after it was closed.
Here's how my function looked like:
private func showDocumentInteractionController(url: URL) {
let documentInteractionController = UIDocumentInteractionController(url: url)
documentInteractionController.presentOptionsMenu(from: view.frame, in: view, animated: true)
documentInteractionController.delegate = self
}
The fix is to make sure that UIDocumentInteractionController is alive after it closes:
class ViewController: UIViewController, UIDocumentInteractionControllerDelegate {
private let documentInteractionController = UIDocumentInteractionController()
private func showDocumentInteractionController(url: URL) {
documentInteractionController.url = url
documentInteractionController.presentOptionsMenu(from: view.frame, in: view, animated: true)
documentInteractionController.delegate = self
}
}

This should work which shows a PDF file:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [self] in
guard let path = Bundle.main.url(forResource: "YOUR_PDF_FILE_NAME_WITHOUT_EXTENSION", withExtension: "pdf") else { return }
let documentInteractionController = UIDocumentInteractionController.init(url: path)
documentInteractionController.delegate = self
documentInteractionController.presentPreview(animated: true)
}
}
}
extension ViewController: UIDocumentInteractionControllerDelegate {
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self
}
}

Related

Is there a delegate method that gets called before the UIDocumentInteractionController share sheet pop ups

Please can someone help.I would like to add an alert message once a user clicks on the share button on a pdf viewer, i'm using UIDocumentInteractionController to preview the pdf document. and i wanted to know if there are any delegate methods or functions that i can override, where i can add my alert before opening the sharing sheet?[![enter image description here][1]][1]
class ViewController: UIViewController {
var documentController : UIDocumentInteractionController!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction private func buttonTapped(_ sender: Any) {
guard let fileURL = Bundle.main.url(forResource: "infomation", withExtension: "pdf") else {return}
documentController = UIDocumentInteractionController.init(url: fileURL)
documentController.delegate = self
documentController.presentPreview(animated: true)
}
}
extension ViewController: UIDocumentInteractionControllerDelegate {
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self
}
func documentInteractionControllerWillPresentOpenInMenu(_ controller: UIDocumentInteractionController) {
}
func documentInteractionControllerWillPresentOptionsMenu(_ controller: UIDocumentInteractionController) {
}
func documentInteractionController(_ controller: UIDocumentInteractionController, willBeginSendingToApplication application: String?) {
}
}
but none of them get called when i click on the share button, even though i have set the delegate to be my view controller.is there a way that i can do this ?
Explanation:
There isn't a way to do that without hacking it together by iterating all the sub-views and override the button's action and target.
One clean way is to create your own preview controller like shown below. If you add the QLPreviewController to UINavigationController, it will automatically create a bottom toolbar which you don't want. To work around this, you add it as a child controller of a regular UIViewController and create a custom navigation bar.
You can also use the UTI for the UIActivityItem to determine how the item can be shared. I've only implemented the preview and basic sharing capabilities, but the rest should be super easy to do, and at least it's more customizable than UIDocumentInteractionController since you have full control of everything.
Now for the code that does all of this:
//
// ViewController.swift
// CustomDocumentController
//
// Created by brandon on 2022-01-13.
//
import UIKit
import QuickLook
import MobileCoreServices
import UniformTypeIdentifiers
private class PreviewItem: NSObject, QLPreviewItem {
var previewItemURL: URL?
var previewItemTitle: String?
init(title: String, url: URL) {
super.init()
previewItemURL = url
previewItemTitle = title
}
}
class DocumentInteractionController: UIViewController {
private let url: URL
private lazy var navigationBar: UINavigationBar = {
let navigationBar = UINavigationBar()
let navigationItem = UINavigationItem(title: name ?? "Document")
navigationItem.hidesBackButton = true
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(onDone))
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(onShare)) //UIImage(systemName: "square.and.arrow.up")
navigationBar.pushItem(navigationItem, animated: false)
return navigationBar
}()
private lazy var previewController: QLPreviewController = {
let previewController = QLPreviewController()
previewController.title = name
previewController.dataSource = self
previewController.delegate = self
previewController.reloadData()
return previewController
}()
var uti: String? {
didSet {
previewController.reloadData()
}
}
var name: String? {
didSet {
previewController.title = name
navigationBar.topItem?.title = name
}
}
init(url: URL) {
self.url = url
super.init(nibName: nil, bundle: nil)
//super.init(rootViewController: self.previewController)
self.modalPresentationStyle = .fullScreen
name = (try? url.resourceValues(forKeys: [.localizedNameKey]))?.localizedName
uti = (try? url.resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
if uti == nil {
if #available(iOS 15.0, *) {
uti = UTType.url.identifier
} else {
uti = String(kUTTypeURL)
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
addChild(previewController)
previewController.didMove(toParent: self)
let separator = UIView()
separator.backgroundColor = .lightGray
view.addSubview(navigationBar)
view.addSubview(separator)
view.addSubview(previewController.view)
NSLayoutConstraint.activate([
navigationBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
navigationBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
separator.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
separator.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
separator.topAnchor.constraint(equalTo: navigationBar.bottomAnchor),
separator.heightAnchor.constraint(equalToConstant: 1.0 / UIScreen.main.scale),
previewController.view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
previewController.view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
previewController.view.topAnchor.constraint(equalTo: separator.bottomAnchor),
previewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
[navigationBar, separator, previewController.view].forEach({
$0?.translatesAutoresizingMaskIntoConstraints = false
})
navigationBar.barTintColor = previewController.view.backgroundColor
view.backgroundColor = previewController.view.backgroundColor
}
}
extension DocumentInteractionController: QLPreviewControllerDelegate, QLPreviewControllerDataSource {
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return PreviewItem(title: name ?? "Document", url: url)
}
}
extension DocumentInteractionController {
#objc
private func onDone() {
self.dismiss(animated: true, completion: nil)
}
#objc
private func onShare() {
let activityViewController = UIActivityViewController(activityItems: [url],
applicationActivities: nil)
activityViewController.excludedActivityTypes = [.assignToContact]
if UIDevice.current.userInterfaceIdiom == .pad {
activityViewController.popoverPresentationController?.sourceView = self.view
}
self.present(activityViewController, animated: true, completion: nil)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = Bundle.main.url(forResource: "Testing", withExtension: ".md")
let doc = DocumentInteractionController(url: url!)
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.present(doc, animated: true, completion: nil)
}
}
}
Screenshots:

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

How to read a URL into an AVURLAsset

I am trying to create an AVURLAsset like so:
class TrimFootageViewController: UIViewController {
var movieURL:URL?
override func viewWillAppear(_ animated: Bool) {
playerView.playerLayer.player = player
super.viewWillAppear(animated)
self.thumbnailImage = setThumbnailFrom(path: movieURL!)
print(type(of: self.movieURL!))
asset = AVURLAsset(url: self.movieURL!, options: nil)
print(asset ?? "couldn't get asset")
}
This does not work throwing an Error (lldb) on a another class: Thread 1: EXC_BREAKPOINT (code=1, subcode=0x100318b4c). Additionally it doesn't print the asset so I don't believe its being set right.
However when I use:
class TrimFootageViewController: UIViewController {
var movieURL:URL?
override func viewWillAppear(_ animated: Bool) {
playerView.playerLayer.player = player
super.viewWillAppear(animated)
self.thumbnailImage = setThumbnailFrom(path: movieURL!)
print(type(of: self.movieURL!))
guard let movieURL = URL(string: "https://devimages-cdn.apple.com/samplecode/avfoundationMedia/AVFoundationQueuePlayer_HLS2/master.m3u8") else {
return
}
asset = AVURLAsset(url: movieURL, options: nil)
print(asset ?? "couldn't get asset")
}
it works and correctly prints <AVURLAsset: 0x101b00210, URL = https://devimages-cdn.apple.com/samplecode/avfoundationMedia/AVFoundationQueuePlayer_HLS2/master.m3u8>
self.movieURL! and movieURL both have the same type of URL when printed. Also note that I am settings self.movieURL like so in the previous controller's segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "TrimFootage_Segue" {
let controller = segue.destination as! TrimFootageViewController
controller.movieURL = self.videoRecorded
}
}
how do I properly set the movieURL asset in the AVURLAsset call such that it can be instantiated?
By looking at your code it seems like movieURL is filePath as setThumbnailFrom(path: movieURL!) is working fine. May be this could be the reason.
You can avoid crashing by applying if-let check as:
class TrimFootageViewController: UIViewController {
var movieURL: URL?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
playerView.playerLayer.player = player
// Just check whether self.movieURL is filePath or URL
// For "setThumbnailFrom" passing as file path
// For "AVURLAsset(url: movURL, options: nil)" passing as URL
self.thumbnailImage = setThumbnailFrom(path: self.movieURL!) // Passing as filePath
if let movURL = self.movieURL as? URL, let asset = AVURLAsset(url: movURL, options: nil) {
print(asset)
} else {
print("Not able to load asset")
}
}
}
Make sure you are sending URL from previous screen:
let controller = segue.destination as! TrimFootageViewController
controller.movieURL = self.videoRecorded
In TrimFootageViewController , define a var movieURLString = "".
In the previous controller's segue:
set movieURLString instead of movieURL.
Then, use your second way init movieURL.
Maybe ok.
I have updated your code. Please have a look. It will not crash anymore and also please check that you are sending the URL(it cannot be nil) from the previous controller:
class TrimFootageViewController: UIViewController {
var movieURL: URL?
override func viewWillAppear(_ animated: Bool) {
playerView.playerLayer.player = player
super.viewWillAppear(animated)
if let mURL = movieURL {
self.thumbnailImage = setThumbnailFrom(path: mURL)
print(type(of: mURL))
asset = AVURLAsset(url: mURL, options: nil)
print(asset ?? "couldn't get asset")
}
}

How to access created pdf document on iOS?

My app has an action that creates pdf file. After creating file on simulator I can access the created file on Mac but on iPhone I can not. Even I have searched but couldn't find. On Acrobat Reader neither.
So what can I do with this created pdf file? Should I convert it ePub or something?
This is my code for creating pdf:
func createPdfFromView(_ imageView: UIImageView, saveToDocumentsWithFileName fileName: String)
{
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, imageView.bounds, nil)
UIGraphicsBeginPDFPage()
let pdfContext = UIGraphicsGetCurrentContext()
if (pdfContext == nil)
{
return
}
imageView.layer.render(in: pdfContext!)
UIGraphicsEndPDFContext()
}
#IBAction func createPdfAction(_ sender: UIButton) {
createPdfFromView(ImageDisplay, saveToDocumentsWithFileName: "")
}
Write the PDF Data as a file in local path
private var pdfData: NSMutableData!
private var filPath: NSString!
private var docController: UIDocumentInteractionController!
func writeDataAsFile()
{
//Search for local path
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let directoryPath = paths[0] as NSString
//Appending file name to be saved in directory path
filPath = directoryPath.appendingPathComponent("TestPDF.pdf") as NSString!
//Check file is already exist in path
let isFileExists = FileManager.default.fileExists(atPath: filPath as String)
if(!isFileExists)
{
//pdfData is NSData/NSMutableData, Write to the filepath
pdfData.write(toFile: filPath as String, atomically: true)
}
}
UIDocumentInteractionController provides in-app support for managing user interactions with files in the local system
#IBAction func goPDF(sender: UIButton) {
docController = UIDocumentInteractionController.init(url: NSURL.fileURL(withPath: filPath as String, isDirectory: true))
docController.delegate = self
docController.presentOpenInMenu(from: CGRect.zero, in: self.view, animated: true)
}
UIDocumentInteractionControllerDelegate to handle document interactions
func documentInteractionController(_ controller: UIDocumentInteractionController, willBeginSendingToApplication application: String?) {
print("willBeginSendingToApplication")
}
func documentInteractionController(_ controller: UIDocumentInteractionController, didEndSendingToApplication application: String?) {
print("didEndSendingToApplication")
}
func documentInteractionControllerDidDismissOpenInMenu(_ controller: UIDocumentInteractionController) {
print("documentInteractionControllerDidDismissOpenInMenu")
}
Using delegate in your ViewController should conforms the UIDocumentInteractionControllerDelegate like this, for example, in ViewController
import UIKit
class FirstViewController: UIViewController, UIDocumentInteractionControllerDelegate {
}
For open PDF in iBooks the menu will provide "Import with iBooks" option. This option will lead to open the PDF in iBooks after importing.

warning: attempt to present whose view is not in the window hierarchy

I have a UITabView in UIViewController, all tab items are linked to other UIViewControllers. I have written a swift code of downloading a file through internet. when I select second tabItem, this code runs well, it downloads and previews the downloaded file, Then when I click on first tabItem and then again click on second tabItem; file downloads well but it doesn't show any preview instead xCode gives me a warning message:
What I want is download file and preview file both should work when I again click on the second tabItem. whatever the code is.
warning: attempt to present QLPreviewController on KPIViewController whose view is not in the window hierarchy
I have found many solutions on the internet but it didn't work
first solution says to use
let viewer = UIDocumentInteractionController(URL: NSURL(fileURLWithPath: path))
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(viewer, animated: true, completion: nil)
but this function
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(viewer, animated: true, completion: nil)
do not accept
UIDocumentInteractionController
second solution says to override the existing presentViewController function to
override func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
let APP_DELEGATE = UIApplication.sharedApplication().delegate
var presentedModalVC:UIViewController = (APP_DELEGATE!.window?!.rootViewController?.presentedViewController)!
if presentedModalVC == true {
while((presentedModalVC.presentedViewController) != nil){
presentedModalVC = presentedModalVC.presentedViewController!
}
presentedModalVC.presentViewController(viewControllerToPresent, animated: flag, completion: nil)
}
else{
APP_DELEGATE?.window!!.rootViewController?.presentViewController(viewControllerToPresent, animated: flag, completion: nil)
}
}
I tried this but it also needs a UIViewController in its parameters where I have UIDocumentInteractionController
I know these function cannot accept UIDocumentInteractionController type viewController.
here is my whole swift code:
// KPIViewController.swift
// download
//
// Created by me on 15/03/2016.
// Copyright © 2016 me. All rights reserved.
//
import UIKit
class KPIViewController: UIViewController,UITabBarDelegate, NSURLSessionDownloadDelegate, UIDocumentInteractionControllerDelegate{
#IBOutlet weak var tabBar1: UITabBar!
#IBOutlet weak var login_Item: UITabBarItem!
#IBOutlet weak var QAreport_Item: UITabBarItem!
#IBOutlet weak var KpiWebView: UIWebView!
#IBOutlet weak var progressView: UIProgressView!
var downloadTask: NSURLSessionDownloadTask!
var backgroundSession: NSURLSession!
var downloadReport:Bool!
var AuditCodeOfDashboardCell:String?
var AuditCodeForPDF:String?
let isDirectory: ObjCBool = false
override func viewDidLoad() {
super.viewDidLoad()
self.progressView.hidden = true
downloadReport = false
// Do any additional setup after loading the view.
self.tabBar1.delegate = self
}
override func viewDidAppear(animated: Bool) {
self.progressView.hidden = true
downloadReport = false
let backgroundSessionConfiguration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("backgroundSession")
backgroundSession = NSURLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
progressView.setProgress(0.0, animated: false)
var requestURL = NSURL!()
var request = NSURLRequest!()
// loading data from web
if AuditCodeOfDashboardCell != nil{
print(self.AuditCodeOfDashboardCell)
requestURL = NSURL(string:“my URL string&\(AuditCodeOfDashboardCell)”)
request = NSURLRequest(URL: requestURL!)
AuditCodeForPDF = AuditCodeOfDashboardCell
AuditCodeOfDashboardCell = nil
}else{
requestURL = NSURL(string:“my URL string”)
request = NSURLRequest(URL: requestURL!)
}
KpiWebView.loadRequest(request)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
print("selected tabItem: \(item.tag)")
switch (item.tag) {
case 1:
let loginVC = self.storyboard!.instantiateViewControllerWithIdentifier("loginViewController") as! LoginView
presentViewController(loginVC, animated: true, completion: nil)
break
case 2:
if AuditCodeForPDF != nil{
downloadReport = true
let url = NSURL(string: “my URL string&\(AuditCodeForPDF)”)!
urlToDownload = url
}
// if let resultController = storyboard!.instantiateViewControllerWithIdentifier(“2”) as? QAReportViewController {
// presentViewController(resultController, animated: true, completion: nil)
// }
break
default:
break
}
if downloadReport == true{
let url = NSURL(string: “my URL string&\(AuditCodeForPDF)”)!
downloadTask = backgroundSession.downloadTaskWithURL(url)
self.progressView.hidden = false
downloadTask.resume()
downloadReport = false
}
}
// - - Handling download file- - - - - - - - -
func URLSession(session: NSURLSession,
downloadTask: NSURLSessionDownloadTask,
didFinishDownloadingToURL location: NSURL){
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let documentDirectoryPath:String = path.first!
let fileManager = NSFileManager()
var destinationURLForFile = NSURL(fileURLWithPath: documentDirectoryPath.stringByAppendingString("/Report.pdf"))
if fileManager.fileExistsAtPath(destinationURLForFile.path!){
// showFileWithPath(destinationURLForFile.path!)
do{
try fileManager.removeItemAtPath(destinationURLForFile.path!)
destinationURLForFile = NSURL(fileURLWithPath: documentDirectoryPath.stringByAppendingString("/Report.pdf"))
}catch{
print(error)
}
}
do {
try fileManager.moveItemAtURL(location, toURL: destinationURLForFile)
// show file
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.showFileWithPath(destinationURLForFile.path!)
})
}catch{
print("An error occurred while moving file to destination url")
}
}
func showFileWithPath(path: String){
let isFileFound:Bool? = NSFileManager.defaultManager().fileExistsAtPath(path)
if isFileFound == true{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let viewer = UIDocumentInteractionController(URL: NSURL(fileURLWithPath: path))
viewer.delegate = self
viewer.presentPreviewAnimated(true)
})
}
}
func URLSession(session: NSURLSession,
downloadTask: NSURLSessionDownloadTask,
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64){
progressView.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
}
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController{
return self
}
func documentInteractionControllerDidEndPreview(controller: UIDocumentInteractionController) {
print("document preview ends")
}
}
I cannot find any proper solution that solve my problem. I am new with swift
please anyone on help me. Thanks in advance
UIDocumentInteractionController is not kind of UIViewController. So you cannot present an UIDocumentInteractionController with presentViewController: method.
Checkout https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIDocumentInteractionController_class/
You can presenting a document preview or options menus with UIDocumentInteractionController.

Resources