简体   繁体   中英

How to display remote document using QLPreviewController in swift

I am using QLPreviewController to preview documents. But i do not know how to display document stored on a server.

You can't. QuickLook only works for local resource files. You would need to download the data asynchronously first, save it to the document directory or to a temporary folder and present the QLPreviewController from the main thread when finished:

edit/update:

Xcode 11.3.1 • Swift 5.1


ViewController.swift

import UIKit
import QuickLook

class ViewController: UIViewController, QLPreviewControllerDelegate, QLPreviewControllerDataSource {
    let previewController = QLPreviewController()
    var previewItems: [PreviewItem] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        let url = URL(string:"https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf")!
        quickLook(url: url)
    }
    func numberOfPreviewItems(in controller: QLPreviewController) -> Int { previewItems.count }
    func quickLook(url: URL) {
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else {
                //  in case of failure to download your data you need to present alert to the user
                self.presentAlertController(with: error?.localizedDescription ?? "Failed to download the pdf!!!")
                return
            }
            // you neeed to check if the downloaded data is a valid pdf
            guard
                let httpURLResponse = response as? HTTPURLResponse,
                let mimeType = httpURLResponse.mimeType,
                mimeType.hasSuffix("pdf")
            else {
                print((response as? HTTPURLResponse)?.mimeType ?? "")
                self.presentAlertController(with: "the data downloaded it is not a valid pdf file")
                return
            }
            do {
                // rename the temporary file or save it to the document or library directory if you want to keep the file
                let suggestedFilename = httpURLResponse.suggestedFilename ?? "quicklook.pdf"
                var previewURL = FileManager.default.temporaryDirectory.appendingPathComponent(suggestedFilename)
                try data.write(to: previewURL, options: .atomic)   // atomic option overwrites it if needed
                previewURL.hasHiddenExtension = true
                let previewItem = PreviewItem()
                previewItem.previewItemURL = previewURL
                self.previewItems.append(previewItem)
                DispatchQueue.main.async {
                    UIApplication.shared.isNetworkActivityIndicatorVisible = false
                    self.previewController.delegate = self
                    self.previewController.dataSource = self
                    self.previewController.currentPreviewItemIndex = 0
                    self.present(self.previewController, animated: true)
                 }
            } catch {
                print(error)
                return
            }
        }.resume()
        UIApplication.shared.isNetworkActivityIndicatorVisible = true
    }
    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem { previewItems[index] }
    func presentAlertController(with message: String) {
         // present your alert controller from the main thread
        DispatchQueue.main.async {
            UIApplication.shared.isNetworkActivityIndicatorVisible = false
            let alert = UIAlertController(title: "Alert", message: message, preferredStyle: .alert)
            alert.addAction(.init(title: "OK", style: .default))
            self.present(alert, animated: true)
        }
    }
}

ExtensionsURL.swift

extension URL {
    var hasHiddenExtension: Bool {
        get { (try? resourceValues(forKeys: [.hasHiddenExtensionKey]))?.hasHiddenExtension == true }
        set {
            var resourceValues = URLResourceValues()
            resourceValues.hasHiddenExtension = newValue
            try? setResourceValues(resourceValues)
        }
    }
}

PreviewItem.swift

import QuickLook
class PreviewItem: NSObject, QLPreviewItem {
    var previewItemURL: URL?
}

You just need to subclass the QLPreviewController and add your functionality. I did it in my case, It waits for files to download from the server and then load those automatically.

My PreviewController Class

import UIKit
import QuickLook

class JSQuickPreviewController: QLPreviewController, QLPreviewControllerDataSource {

    var fileName: String = ""
    var url: URL?

    private var previewItem : PreviewItem!
    private let activityIndicator: UIActivityIndicatorView = {
        let activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        activityIndicator.hidesWhenStopped = true
        activityIndicator.tintColor = .black
        activityIndicator.color = .black
        activityIndicator.startAnimating()
        return activityIndicator
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.dataSource = self;
        self.view.addSubview(activityIndicator)
        activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

        self.title = fileName;
        self.downloadfile(fileName: fileName, itemUrl: url)
        
        self.hideErrorLabel();
        // Do any additional setup after loading the view.
    }
    
    @objc
    func hideErrorLabel() {
        
        var found = false
        for v in self.view.allViews.filter({ $0 is UILabel }) {
            v.isHidden = true
            found = true
        }
        
        if !found {
            self.perform(#selector(hideErrorLabel), with: nil, afterDelay: 0.1);
        }

    }
    
    private func downloadfile(fileName:String, itemUrl:URL?){
        
        self.previewItem = PreviewItem()
        let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let destinationUrl = documentsDirectoryURL.appendingPathComponent(fileName)
        if FileManager.default.fileExists(atPath: destinationUrl.path) {
            self.previewItem.previewItemURL = destinationUrl;
            self.loadFile()
        }
        else {
            DispatchQueue.main.async(execute: {
                if let itemUrl = itemUrl {
                    
                    URLSession.shared.downloadTask(with: itemUrl, completionHandler: { (location, response, error) -> Void in
                        if error != nil {
                            for v in self.view.allViews.filter({ $0 is UILabel }) {
                                v.isHidden = false
                                (v as? UILabel)?.text = error?.localizedDescription
                            }
                        } else {
                            guard let tempLocation = location, error == nil else { return }
                            try? FileManager.default.moveItem(at: tempLocation, to: destinationUrl)
                            self.previewItem.previewItemURL = destinationUrl;
                            self.loadFile()
                        }
                    }).resume()
                }
            })
        }
    }
    
    func loadFile() {
    
        DispatchQueue.main.async {
            self.activityIndicator.isHidden = true
            self.reloadData()
        }
    }
    
    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
        return previewItem == nil ? 0 : 1
    }
    
    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
        return previewItem
    }
}

extension UIView {
    var allViews: [UIView] {
        var views = [self]
        subviews.forEach {
            views.append(contentsOf: $0.allViews)
        }
        return views
    }
}

How to use it

 let quickPreviewController = JSQuickPreviewController()
 quickPreviewController?.url = fileURL
 quickPreviewController?.fileName = filename
 self.show(quickPreviewController, animated: true)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM