How to display remote document using QLPreviewController in swift
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?
}
How to display pdf from network in iOS
You need to save the file to disk first and then you can present the pdf. There is no way to present it with QuickLook if the file is in a remote location. The file is saved in the temporary directory. Here is an example view controller showing how it could be done.
Swift 5:
import UIKit
import QuickLook
class ViewController: UIViewController, QLPreviewControllerDataSource {
// Remote url pdf I found on google
let itemURL = URL(string: "https://www.ets.org/Media/Tests/GRE/pdf/gre_research_validity_data.pdf")!
var fileURL: URL?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let quickLookController = QLPreviewController()
quickLookController.dataSource = self
do {
// Download the pdf and get it as data
// This should probably be done in the background so we don't
// freeze the app. Done inline here for simplicity
let data = try Data(contentsOf: itemURL)
// Give the file a name and append it to the file path
fileURL = FileManager().temporaryDirectory.appendingPathComponent("sample.pdf")
if let fileUrl = fileURL {
// Write the pdf to disk in the temp directory
try data.write(to: fileUrl, options: .atomic)
}
// Make sure the file can be opened and then present the pdf
if QLPreviewController.canPreview(fileUrl as QLPreviewItem) {
quickLookController.currentPreviewItemIndex = 0
present(quickLookController, animated: true, completion: nil)
}
} catch {
// cant find the url resource
}
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return fileURL! as QLPreviewItem
}
}
Swift 3:
import UIKit
import QuickLook
class ViewController: UIViewController, QLPreviewControllerDataSource {
// Remote url pdf I found on google
let itemURL = URL(string: "https://www.ets.org/Media/Tests/GRE/pdf/gre_research_validity_data.pdf")!
var fileURL = URL(string: "")
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let quickLookController = QLPreviewController()
quickLookController.dataSource = self
do {
// Download the pdf and get it as data
// This should probably be done in the background so we don't
// freeze the app. Done inline here for simplicity
let data = Data(contentsOf: itemURL)
// Give the file a name and append it to the file path
fileURL = FileManager().temporaryDirectory.appendingPathComponent("sample.pdf")
// Write the pdf to disk
try data?.write(to: fileURL!, options: .atomic)
// Make sure the file can be opened and then present the pdf
if QLPreviewController.canPreview(fileUrl as QLPreviewItem) {
quickLookController.currentPreviewItemIndex = 0
present(quickLookController, animated: true, completion: nil)
}
} catch {
// cant find the url resource
}
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return fileURL! as QLPreviewItem
}
}
Here is the file showing in the simulator. Using a sample project with just that code.
Is it possible to view a PDF file from an URL link using Quick Look Framework
I have resolved by using UIDocumentInteractionController
UIDocumentInteractionController *viewer = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:path]];
viewer.delegate = self;
[viewer presentPreviewAnimated:YES];
iOS QLPreviewController load file from remote server URL
To show any file that supports QLPreviewController
, the url should
be the fileURL.
(id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index
It always returns fileURL
- if you will use any other URL, it will crash. After downloading is over save the file in documents directory and then push to preview.
- (void)saveFileInDocDirectoryWithFileName:(NSString *)title{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath_ = [paths objectAtIndex:0];
NSString *filePath = [ docPath_ stringByAppendingPathComponent:title];
self.fileURL = [NSURL fileURLWithPath:filePath];
[self pushToPreViewWithURL:fileURL];
}
- (void)pushToPreViewWithURL:(NSURL *)filePathURL{
QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
// start previewing the document at the current section index
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.navigationController pushViewController:previewController animated:YES];
}];
}
Then in delegate method, return fileURL
:
- (id)previewController:(QLPreviewController *)previewController previewItemAtIndex:(NSInteger)idx {
return self.fileURL;
}
QLPreviewController showing file then going blank in SwiftUI
I finally got it to work – big thanks to Leo Dabus for his help in the comments.
Here's my currently working code:
@State private var isShowingDoc = false
@State private var isLoadingFile = false
@State private var fileURL: URL?
var body: some View {
Button {
let destination: DownloadRequest.Destination = { _, _ in
let documentsURL = FileManager.default.documentsDirectory
.appendingPathComponent(document.id.string)
.appendingPathComponent(document.name ?? "file.jpg")
return (documentsURL, [.removePreviousFile, .createIntermediateDirectories])
}
isLoadingFile = true
AF
.download(url, to: destination)
.responseURL { (response) in
self.isLoadingFile = false
guard let url = response.fileURL else { return }
isShowingDoc = true
self.fileURL = url
}
} label: {
VStack {
Text("download")
if isLoadingFile {
ActivityIndicator(style: .medium)
}
}
}
.sheet(isPresented: $isShowingDoc, onDismiss: { isShowingDoc = false }) {
QuickLookView(url: fileURL!)
}
}
with this QuickLookView
: (mostly unchanged)
struct QuickLookView: UIViewControllerRepresentable {
var url: URL
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func updateUIViewController(_ viewController: UINavigationController, context: UIViewControllerRepresentableContext<Self>) {
(viewController.topViewController as? QLPreviewController)?.reloadData()
}
func makeUIViewController(context: Context) -> UINavigationController {
let controller = QLPreviewController()
controller.dataSource = context.coordinator
controller.reloadData()
return UINavigationController(rootViewController: controller)
}
class Coordinator: NSObject, QLPreviewControllerDataSource {
var parent: QuickLookView
init(_ qlPreviewController: QuickLookView) {
self.parent = qlPreviewController
super.init()
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
self.parent.url as QLPreviewItem
}
}
}
As you can see, there's hardly any difference to my code from when I asked the question. Yesterday night, the fileURL
was always nil
for an unclear reason; yet, now it started working just fine. In exchange, the remote images in my list (not shown here) stopped working even though I haven't touched them, haha.
I don't know what's going on and what I even changed to make it work, but it works and I won't complain!
QLPreviewController not loading multiple files
Turns out, I misunderstood how multiple items are loaded in a QLPreviewController
.
I thought, the user had to scroll vertically and see the side bar with thumbnails. This is only for pages within a single document, however.
I had to scroll horizontally.
Thanks to @matt, who cleared this up for me.
Does `QLPreviewItem` works with local files only?
QLPreviewItem
works with local file URLs only, it won't download your file for you if you provide it a remote file url.
You need to download your file to your app's local file system before presenting QLPreviewController
and provide it that local file url.
For advanced usage - multiple urls, dataSource, delegate handling - you should watch Quick Look Previews from the Ground Up from WWDC 2018.
iOS/Swift: QLPreviewController shows blank page in iOS11
You did not add the current viewController as the parent class of the QLPreviewController()
Just add QLController.didMove(toParentViewController: self)
after adding it to the self.view
I think this should solve your issue.
Related Topics
Xcode Error While Validation - "Your Binary Is Not Optimized for iPhone 5"
How to Completely Hide the Status Bar in iOS Using Cordova
Open Links in Safari Instead of Uiwebview
Add Cocoapods to Tests Target Too
Memory Leak When Using Nsurlsession.Downloadtaskwithurl
Ios: Is Core Graphics Implemented on Top of Opengl
How to Position Views on Top of Each Other
Abpeoplepickernavigationcontroller Changes with iOS8
When Does Awakefromnib Get Called
Accessing Variables from Another Viewcontroller in Swift
Codesign Returned Unknown Error -1=Ffffffffffffffff
How to Create a Uiimage from the Current Graphics Context
Get Pixel Value from Cvpixelbufferref in Swift
Setting Up Uiscrollview to Swipe Between 3 View Controllers