develop

Preview (PDF, EXCEL) 본문

iOS

Preview (PDF, EXCEL)

pikachu987 2020. 12. 28. 10:18
반응형

QLPreviewController를 사용하면 엑셀, 이미지, PDF, 집파일 등을 쉽게 보여줄 수 있다.

 

import QuickLook
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        DispatchQueue.main.async {
            let previewController = QLPreviewController()
            previewController.dataSource = self
            self.present(previewController, animated: true, completion: nil)
        }
    }
}
extension ViewController: QLPreviewControllerDataSource {
    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
        return 1
    }
    
    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
        guard let url = Bundle.main.url(forResource: "Financial Sample", withExtension: "xlsx") else { fatalError() }
        return url as QLPreviewItem
    }
}

하지만 위와 같이 보여지는 것은 내부의 파일이고 원격저장소에 있는 파일을 보여주려면 파일을 저장을 해야 한다.

 

 

import QuickLook
class ViewController: UIViewController {
    
    private var tempURL: URL?
    
    func showPreview() {
        let previewController = QLPreviewController()
        previewController.delegate = self
        previewController.dataSource = self
        self.present(previewController, animated: true, completion: nil)
        
        guard let url = URL(string: "https://images.apple.com/environment/pdf/Apple_Environmental_Responsibility_Report_2017.pdf") else { return }
        DispatchQueue.global().async {
            URLSession.shared.dataTask(with: url) { data, response, error in
                DispatchQueue.main.async {
                    guard let data = data, let urlPath = response?.url?.absoluteString.replacingOccurrences(of: "%20", with: "_"), let name = urlPath.components(separatedBy: "/").last, error == nil else {
                        return
                    }
                    let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(name)
                    try? data.write(to: tempURL, options: .atomic)
                    self.tempURL = tempURL
                    previewController.reloadData()
                }
            }.resume()
        }
    }
}
extension ViewController: QLPreviewControllerDelegate {
    func previewControllerDidDismiss(_ controller: QLPreviewController) {
        guard let tempURL = self.tempURL else { return }
        try? FileManager.default.removeItem(at: tempURL)
    }
}
extension ViewController: QLPreviewControllerDataSource {
    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
        return self.tempURL == nil ? 0 : 1
    }
    
    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
        guard let url = self.tempURL else { fatalError() }
        return url as QLPreviewItem
    }
}

 

파일을 다운받은 다음 FileManager를 이용해 내부에 파일을 저장하고 저장한 위치의 URL을 가져와 보여준다. 그리고 화면이 Dismiss되면 저장한 파일을 삭제한다.

 

 

 

여러 파일을 보여줄수도 있다. 여러 파일을 보여주면 UICollectionView처럼 Horizontal Scroll이 된다.

 

 

QLPreviewController를 상속받아서 사용하는 방법도 있다.

 

 

QLPreviewController를 상속받으면 다운받는 중일때 Indicator를 만들어 표시하게 할 수 있다.

간단하게 상속받아서 만들어 보았다.

class DocumentPreviewController: QLPreviewController {
    struct Preview {
        var originURL: URL
        var previewURL: URL?
        var isTempSaved = false
        
        init?(url: URL?) {
            guard let url = url else { return nil }
            guard let scheme = url.scheme else { return nil }
            self.originURL = url
            if scheme == "file" {
                self.previewURL = url
            }
        }
    }
    
    private var previews = [Preview]()
    
    private let activityIndicatorView: UIActivityIndicatorView = {
        let activityIndicatorView = UIActivityIndicatorView(style: .large)
        activityIndicatorView.color = .gray
        activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
        return activityIndicatorView
    }()
    
    convenience init(urls: [URL?]) {
        self.init()
        
        self.previews = urls.compactMap({ Preview(url: $0) })
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.addSubview(activityIndicatorView)
        self.view.addConstraints([
            NSLayoutConstraint(item: self.view, attribute: .centerX, relatedBy: .equal, toItem: activityIndicatorView, attribute: .centerX, multiplier: 1, constant: 0),
            NSLayoutConstraint(item: self.view, attribute: .centerY, relatedBy: .equal, toItem: activityIndicatorView, attribute: .centerY, multiplier: 1, constant: 0),
            ])
        self.activityIndicatorView.hidesWhenStopped = true
        self.activityIndicatorView.startAnimating()
        
        self.delegate = self
        self.dataSource = self
        self.reloadData()
        
        let group = DispatchGroup()
        self.previews.enumerated().forEach { (enumerated) in
            if enumerated.element.previewURL == nil {
                DispatchQueue.global().async {
                    group.enter()
                    URLSession.shared.dataTask(with: enumerated.element.originURL) { data, response, error in
                        let urlPath = response?.url?.absoluteString.replacingOccurrences(of: "%20", with: "_")
                        guard let data = data, let name = urlPath?.components(separatedBy: "/").last else {
                            group.leave()
                            return
                        }
                        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(name)
                        do {
                            try data.write(to: tempURL, options: .atomic)
                            self.previews[enumerated.offset].previewURL = tempURL
                            self.previews[enumerated.offset].isTempSaved = true
                            group.leave()
                        } catch {
                            group.leave()
                        }
                    }.resume()
                }
            }
        }
        
        group.notify(queue: DispatchQueue.main) {
            self.activityIndicatorView.stopAnimating()
            self.reloadData()
        }
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        self.navigationItem.setHidesBackButton(true, animated: true)
        self.navigationItem.backBarButtonItem = nil
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.backAction(_:)))
    }
    
    @objc private func backAction(_ sender: UIBarButtonItem) {
        self.dismiss(animated: true, completion: nil)
    }
}
// MARK: QLPreviewControllerDelegate
extension DocumentPreviewController: QLPreviewControllerDelegate {
    func previewControllerDidDismiss(_ controller: QLPreviewController) {
        self.previews.filter({ $0.isTempSaved }).forEach({
            if let url = $0.previewURL {
                try? FileManager.default.removeItem(at: url)
            }
        })
    }
}
// MARK: QLPreviewControllerDataSource
extension DocumentPreviewController: QLPreviewControllerDataSource {
    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
        return self.previews.count
    }
    
    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
        if let url = self.previews[index].previewURL {
            return url as QLPreviewItem
        } else {
            return self.previews[index].originURL as QLPreviewItem
        }
    }
}
let urls = [
    URL(string: "https://images.apple.com/environment/pdf/Apple_Environmental_Responsibility_Report_2017.pdf"),
    Bundle.main.url(forResource: "Financial Sample", withExtension: "xlsx"),
    URL(string: "https://scholar.harvard.edu/files/torman_personal/files/samplepptx.pptx"),
    URL(string: "https://calibre-ebook.com/downloads/demos/demo.docx")
]
let previewController = DocumentPreviewController(urls: urls)
self.present(previewController, animated: true, completion: nil)
반응형

'iOS' 카테고리의 다른 글

Image Face Detector 사진에서 얼굴 찾기  (0) 2020.12.30
Text Gradation 텍스트에 그라데이션 추가  (0) 2020.12.29
PHAsset  (0) 2020.12.29
UDID UUID ADID  (0) 2020.12.27
UIBezierPath Shape (도형 그리기)  (0) 2020.12.27
Comments