【问题标题】:Use VNDocumentCameraViewController as a UIViewControllerRepresentable in a TabView在 TabView 中使用 VNDocumentCameraViewController 作为 UIViewControllerRepresentable
【发布时间】:2021-10-19 01:43:06
【问题描述】:

我将VNDocumentCameraViewController 作为UIViewControllerRepresentable 在名为ScanView 的视图中嵌入TabView 作为第二个屏幕。在关闭VNDocumentCameraViewController(取消或保存扫描)时,我希望选项卡视图返回到我的第一个屏幕。这部分就像一个魅力。

我的问题是,当回到我的VNDocumentCameraViewController 时,我想重新实例化该控制器并重新开始——这是我无法弄清楚如何实现的。

我知道我的ContentView 保留对ScanView 的引用是为什么我的UIViewControllerRepresentable 没有被重新实例化——我该如何手动执行?

代码如下:

import SwiftUI

@main
struct so_VisionKitInTabsApp: App {
        var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State private var tabSelection = 1

    var body: some View {
        TabView(selection: $tabSelection) {
            Text("First View")
                .tabItem { Text("First View") }
                .tag(1)
            ScanView(tabSelection: $tabSelection)
                .tabItem { Text("Scan View") }
                .tag(2)
        }
    }
}

import VisionKit

struct DocumentScanningViewAdapter: UIViewControllerRepresentable {
    typealias UIViewControllerType = VNDocumentCameraViewController

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

    func makeUIViewController(context: Context) -> VNDocumentCameraViewController {
        let vc = VNDocumentCameraViewController()
        vc.delegate = context.coordinator
        return vc
    }
    
    func updateUIViewController(_ uiViewController: VNDocumentCameraViewController, context: Context) { }

    class Coordinator: NSObject, VNDocumentCameraViewControllerDelegate {
        var parent: DocumentScanningViewAdapter
        
        init(parent: DocumentScanningViewAdapter) {
            self.parent = parent
        }
        
        func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
            print("Finished successfully…")
            parent.onDismiss()
        }
        
        func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
            print("Cancelled…")
            resetCoordinator(for: controller)
            parent.onDismiss()
        }
        
        func resetCoordinator(for controller: VNDocumentCameraViewController) {
            controller.delegate = parent.makeCoordinator()
        }
    }
    
}

struct ScanView: View {
    @Binding var tabSelection: Int
    
    var body: some View {
        DocumentScanningViewAdapter(onDismiss: { tabSelection = 1 })
    }
}

【问题讨论】:

    标签: ios swift swiftui swiftui-tabview uiviewcontrollerrepresentable


    【解决方案1】:

    感谢 Philip 在上面的回答,建议我可以使用 .sheet,我想出了这个使用 .fullScreenCover 的新 ScanView

    工作正常,但它会覆盖标签栏(我认为我可以忍受)。

    struct ScanView: View {
        @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
        @State private var showFullscreen = true
        @Binding var tabSelection: Int
        
        var body: some View {
            EmptyView()
                .fullScreenCover(isPresented: $showFullscreen, onDismiss: {
                    presentationMode.wrappedValue.dismiss()
                }) {
                    DocumentScanningViewAdapter(onDismiss: {
                        showFullscreen = false
                        tabSelection = 1
                    })
                }
                .onAppear {
                    showFullscreen = true
                }
        }
    }
    

    【讨论】:

    • 再想一想,也许最好让扫描屏幕全部覆盖——在扫描中更改标签可能不是一个好主意...
    【解决方案2】:

    TabView 是这样工作的:它保存每个选项卡的状态

    你可以破解它,但请不要:当 iOS 用户看到标签视图时,他希望如果他从一个标签切换到另一个并返回,他不会失去任何状态

    只需制作一个按钮,将您的文档选择器显示为.sheet(...) 或使用NavigationLink 推入导航控制器,您的问题就会消失。

    如果您使用其中一种方法,则无需重置控制器的状态,因为每次呈现视图时都会重新创建它

    如果你仍然想这样做,你可以用 UINavigationController 包裹你的控制器,然后在 updateUIViewController 中初始化你自己的控制器

    func makeUIViewController(context: Context) -> UINavigationController {
        let controller = UINavigationController()
        controller.isNavigationBarHidden = true
        return controller
    }
    
    func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {
        let vc = VNDocumentCameraViewController()
        vc.delegate = context.coordinator
        uiViewController.viewControllers = [vc]
    }
    

    updateUIViewController 会在每次需要重新渲染视图时被调用。如果使用TabView,它仍然无法工作,因为状态已保存,您可以将.id(tabSelection) 添加到您的ScanView

    【讨论】:

    • 我猜你是对的 TabView 保存状态 - 但必须有解决方案...有没有办法重新实例化我的 DocumentScanningViewAdapter 的代表?
    • @ASSeeger 请解释您所说的重新实例化代表是什么意思?
    • makeUIViewController(context:) 中,我实例化了我的视图控制器并设置了它的委托(i.d. Coordinator)。我会假设我可以以某种方式重置该视图控制器或其委托——这有望导致我想要的行为。
    • .sheet 的提示是最有帮助的,这就是为什么我选择它作为正确答案的原因。为了将来参考,请参阅上面的答案,我使用.fullScreenCover 解决了我的问题,因为我认为这里比.sheet 更合适。
    猜你喜欢
    • 2021-04-21
    • 1970-01-01
    • 1970-01-01
    • 2020-06-06
    • 2020-02-27
    • 1970-01-01
    • 2022-11-12
    • 2023-01-11
    • 1970-01-01
    相关资源
    最近更新 更多