ios 使用可在SwiftUI中表示的UIViewController呈现UIDocumentInteractionController

lsmepo6l  于 2022-11-19  发布在  iOS
关注(0)|答案(3)|浏览(185)

我正在尽可能使用SwiftUI创建一个新的iOS应用程序。但是,我希望能够生成包含一些数据的PDF。在没有SwiftUI的类似项目中,我可以做到这一点

let docController = UIDocumentInteractionController.init(url: "PATH_TO_FILE")
                        docController.delegate = self
                        self.dismiss(animated: false, completion: {
                            docController.presentPreview(animated: true)
                        })

只要在视图控制器的其他地方我就有这个:

func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
        return self
    }

我准备好了。我不知道的是如何将其应用到UIViewControllerRepresentable并让它在SwiftUI中工作。我的UIViewControllerRepresentable应该成为UIViewController吗?然后我如何设置代理和presentPreview?这会像在标准iOS应用中那样覆盖任何视图并在SwiftUI应用上全屏显示吗?谢谢

oalqel3c

oalqel3c1#

以下是集成UIDocumentInteractionController以便从SwiftUI视图使用的可能方法。

全模块代码。使用Xcode 11.2 / iOS 13.2进行测试

import SwiftUI
import UIKit

struct DocumentPreview: UIViewControllerRepresentable {
    private var isActive: Binding<Bool>
    private let viewController = UIViewController()
    private let docController: UIDocumentInteractionController

    init(_ isActive: Binding<Bool>, url: URL) {
        self.isActive = isActive
        self.docController = UIDocumentInteractionController(url: url)
    }
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<DocumentPreview>) -> UIViewController {
        return viewController
    }
    
    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<DocumentPreview>) {
        if self.isActive.wrappedValue && docController.delegate == nil { // to not show twice
            docController.delegate = context.coordinator
            self.docController.presentPreview(animated: true)
        }
    }
    
    func makeCoordinator() -> Coordintor {
        return Coordintor(owner: self)
    }
    
    final class Coordintor: NSObject, UIDocumentInteractionControllerDelegate { // works as delegate
        let owner: DocumentPreview
        init(owner: DocumentPreview) {
            self.owner = owner
        }
        func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
            return owner.viewController
        }
        
        func documentInteractionControllerDidEndPreview(_ controller: UIDocumentInteractionController) {
            controller.delegate = nil // done, so unlink self
            owner.isActive.wrappedValue = false // notify external about done
        }
    }
}

// Demo of possible usage
struct DemoPDFPreview: View {
    @State private var showPreview = false // state activating preview

    var body: some View {
        VStack {
            Button("Show Preview") { self.showPreview = true }
                .background(DocumentPreview($showPreview, // no matter where it is, because no content
                            url: Bundle.main.url(forResource: "example", withExtension: "pdf")!))
        }
    }
}

struct DemoPDFPreview_Previews: PreviewProvider {
    static var previews: some View {
        DemoPDFPreview()
    }
}
oewdyzsn

oewdyzsn2#

最后我做了如下这样的事情,因为我不能让它在UIViewControllerRepresentable和上面的答案下可靠地工作。你可能需要编辑/扩展它以适合你的用例。

class DocumentController: NSObject, ObservableObject, UIDocumentInteractionControllerDelegate {
    let controller = UIDocumentInteractionController()
    func presentDocument(url: URL) {
        controller.delegate = self
        controller.url = url
        controller.presentPreview(animated: true)
    }

    func documentInteractionControllerViewControllerForPreview(_: UIDocumentInteractionController) -> UIViewController {
        return UIApplication.shared.windows.first!.rootViewController!
    }
}

用法:

struct DocumentView: View {
  @StateObject var documentController = DocumentController()

  var body: some View {
      Button(action: {
          documentController.presentDocument(url: ...)
      }, label: {
          Text("Show Doc")
      })
  }
}
j2cgzkjk

j2cgzkjk3#

使用QLPreviewController
我知道这个问题是关于UIDocumentInteractionController的,但是如果你想给出一个PDF file(例如),你可以使用**QLPreviewController**。

本地文件

显示本地文件:

import SwiftUI

struct DocView: View {
    @State private var buttonPressed: Bool = false

    var body: some View {
        Button {
            buttonPressed = true
        } label: {
            Text("Show PDF file")
        }
        .sheet(isPresented: $buttonPressed) {
            let localURL = Bundle.main.url(forResource: "Example", withExtension: "pdf")!
            PreviewController(url: localURL)
        }
    }
}

远程文件

如果需要显示远程文件,请使用see this gist

预览控件

可表示为QLPreviewController的UIView控制器。

import QuickLook
import SwiftUI

struct PreviewController: UIViewControllerRepresentable {
    @Environment(\.dismiss) private var dismiss

    let url: URL

    func makeUIViewController(context: Context) -> UINavigationController {
        let controller = QLPreviewController()
        controller.dataSource = context.coordinator
        controller.navigationItem.leftBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .done, target: context.coordinator,
            action: #selector(context.coordinator.dismiss)
        )

        let navigationController = UINavigationController(rootViewController: controller)
        return navigationController
    }

    func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}

    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self)
    }

    class Coordinator: QLPreviewControllerDataSource {
        let parent: PreviewController

        init(parent: PreviewController) {
            self.parent = parent
        }

        func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
            return 1
        }

        func previewController(
            _ controller: QLPreviewController,
            previewItemAt index: Int
        ) -> QLPreviewItem {
            return parent.url as NSURL
        }

        @objc func dismiss() {
            parent.dismiss()
        }
    }
}

相关问题