在 TabView 中使用 VNDocumentCameraViewController 作为 UIViewControllerRepresentable
Use VNDocumentCameraViewController as a UIViewControllerRepresentable in a TabView
我在名为 ScanView
的视图中将 VNDocumentCameraViewController
作为 UIViewControllerRepresentable
嵌入到作为第二个屏幕的 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 })
}
}
这就是 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
感谢 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
}
}
}
我在名为 ScanView
的视图中将 VNDocumentCameraViewController
作为 UIViewControllerRepresentable
嵌入到作为第二个屏幕的 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 })
}
}
这就是 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
感谢 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
}
}
}