【问题标题】:QLPreviewController showing file then going blank in SwiftUIQLPreviewController 显示文件然后在 SwiftUI 中变为空白
【发布时间】:2021-05-20 11:22:29
【问题描述】:

我为 UIKit 的 QLPreviewController 添加了一个 UIViewControllerRepresentable,我在 related question 中找到了它:

struct QuickLookView: UIViewControllerRepresentable {
    
    var url: URL
    var onDismiss: (() -> Void) = { }
    
    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
        }
    }
}

在我的应用程序中,我通过 Alamofire 下载了一个文件 (jpg/png/pdf):

let destination: DownloadRequest.Destination = { _, _ in
    let documentsURL = FileManager.default.documentsDirectory
        .appendingPathComponent(document.id.string)
        .appendingPathComponent(document.name ?? "file.jpg")
    return (documentsURL, [.removePreviousFile, .createIntermediateDirectories])
}
AF
    .download(url, to: destination)
    .responseURL { (response) in
        guard let url = response.fileURL else { return }
        self.fileURL = url
        self.isShowingDoc = true
    }

...并将其本地 url 传递给 QuickLookView 以显示它:

@State private var isShowingDoc = false
@State private var fileURL: URL?

var body: some View {
    // ...
    .sheet(isPresented: $isShowingDoc, onDismiss: { isShowingDoc = false }) {
        QuickLookView(url: fileURL!) {
            isShowingDoc = false
        }
    }
}

发生的情况是QuickLookView 作为工作表打开,文件闪烁(显示大约 0.1 秒),然后视图变为空白:

我在 Finder 中检查了应用程序的 Documents 文件夹,该文件在那里并且与传递给 QuickLookView 的 url 匹配。我注意到,当视图打开时,然后我通过 Finder 从文件夹中删除文件,然后视图会抛出一个错误,说没有这样的文件 - 这意味着它在删除之前确实正确读取了它。

注意:我在某处读到 QL 控制器在放置在导航控制器中时出现问题。在我的视图层次结构中,我的视图嵌入在 NavigationView 中——这可能会导致问题吗?

我该如何解决这个问题?

【问题讨论】:

  • 你能分享一下你传递本地网址的方式吗
  • 这适用于本地 pdf、png、jpeg、gif 文件,一切似乎都正常
  • @cedricbahirwe 我已经更新了我的帖子,并包含了我如何获取文件和传递 url 的摘要版本。再次,文件显示在 Finder 中,当我打印 url 时,它正确地将我带到文件。
  • 您可能是在完成下载之前创建视图

标签: swift swiftui quicklook


【解决方案1】:

您只需要在呈现工作表之前更新视图,否则它将无法工作。它可以是按钮标题、不透明度或任何内容。虽然它看起来像一个 hack,但它工作正常。如果有人解释为什么会发生这种情况,以及是否有适当的方法让它在不更新视图的情况下工作,我将非常高兴。


import SwiftUI

struct ContentView: View {
    @State private var fileURL: URL!
    @State private var isDisabled = false
    @State private var isDownloadFinished = false
    @State private var buttonTitle: String = "Download PDF"
    private let url = URL(string: "https://www.dropbox.com/s/bxrhk6194lf0n73/macpro_mid2010-macpro_mid2012.pdf?dl=1")!
    var body: some View {
        Button(buttonTitle) {
            isDisabled = true
            buttonTitle = "Downloading..."
            URLSession.shared.downloadTask(with: url) { location, response, error in
                guard
                    let location = location, error == nil,
                    let suggestedFilename = (response as? HTTPURLResponse)?.suggestedFilename,
                    let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
                else { return }
                fileURL = documentDirectory.appendingPathComponent(suggestedFilename)
                if !FileManager.default.fileExists(atPath: fileURL.path) {
                    do {
                        try FileManager.default.moveItem(at: location, to: fileURL)
                    } catch {
                        print(error)
                    }
                }
                DispatchQueue.main.async {
                    isDownloadFinished = true
                    buttonTitle = ""  // you need to change the view prefore presenting the sheet otherwise it wont work
                }
            }.resume()
        }
        .disabled(isDisabled == true)
        .sheet(isPresented: $isDownloadFinished) {
            isDisabled = false
            isDownloadFinished = false
            fileURL = nil
            buttonTitle = "Download PDF"
        } content: {
            if isDownloadFinished {
                PreviewController(previewItems: [PreviewItem(url: fileURL, title: fileURL?.lastPathComponent)], index: 0)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


import SwiftUI
import QuickLook

struct PreviewController: UIViewControllerRepresentable {
    
    var previewItems: [PreviewItem] = []
    
    var index: Int
    
    func makeCoordinator() -> Coordinator { .init(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.delegate = context.coordinator
        controller.reloadData()
        return .init(rootViewController: controller)
    }
    class Coordinator: NSObject, QLPreviewControllerDataSource, QLPreviewControllerDelegate {
        let previewController: PreviewController
        init(_ previewController: PreviewController) {
            self.previewController = previewController
        }
        func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
            previewController.previewItems.count
        }
        func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
            previewController.previewItems[index]
        }
    }
}

class PreviewItem: NSObject, QLPreviewItem {
    var previewItemURL: URL?
    var previewItemTitle: String?
    init(url: URL? = nil, title: String? = nil) {
        previewItemURL = url
        previewItemTitle = title
    }
}

【讨论】:

    【解决方案2】:

    我终于让它工作了——非常感谢Leo Dabusthe comments 中的帮助。
    这是我当前工作的代码:

    @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!)
        }
    }
    

    有了这个QuickLookView:(大部分不变)

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

    如您所见,我的代码与我提出问题时几乎没有任何区别。昨天晚上,fileURL 一直是nil,原因不明;然而,现在它开始工作得很好。作为交换,我列表中的远程图像(此处未显示)停止工作,即使我没有触摸它们,哈哈。

    我不知道发生了什么,我什至做了什么改变以使它工作,但它工作,我不会抱怨!

    【讨论】:

    • @LeoDabus 此处的活动指示器似乎与您的按钮标题一样,确保它有效...
    猜你喜欢
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-10
    相关资源
    最近更新 更多