1

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

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

我知道我ContentView保留对 的引用ScanView是为什么 myUIViewControllerRepresentable没有被重新实例化——我怎样才能手动做到这一点?

这是代码:

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

2 回答 2

2

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

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

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

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

如果你仍然想这样做,你可以用 a 包装你的控制器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

于 2021-08-17T06:33:13.167 回答
1

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

完成这项工作,但它会覆盖标签栏(我认为我可以忍受)。

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
            }
    }
}
于 2021-08-17T07:33:30.097 回答