[英]How to navigate from a subview of a TabView back to the TabView in SwiftUI?
在 SwiftUI 中,TabView 必须是根视图。 因此,您不能使用 NavigationLink 导航到 TabView。 例如,假设我的应用程序中有四个屏幕。
屏幕 A 是一个 TabView,包含屏幕 B 和屏幕 C。 屏幕 B 是具有 NavigationLink 的列表,可将您带到列表项详细信息(屏幕 D)屏幕 C 是信息视图(在问题中并不重要)屏幕 D 是列表项详细信息屏幕,您必须先导航到屏幕b 到这里。 然而,屏幕 D 有一个按钮,应该在 ViewModel 中执行网络操作,然后在完成后将您带到屏幕 A。
屏幕 D 如何导航回两个级别到根屏幕(屏幕 A)?
我做了这样的把戏,为我工作。
在 SceneDelegate.swift 中,我修改了自动生成的代码。
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
// Trick here.
let nav = UINavigationController(
rootViewController: UIHostingController(rootView: contentView)
)
// I embedded this host controller inside UINavigationController
// window.rootViewController = UIHostingController(rootView: contentView)
window.rootViewController = nav
self.window = window
window.makeKeyAndVisible()
}
注意:我希望在NavigationView
中嵌入TabView
会做同样的工作,但没有奏效,这是做这个技巧的原因。
我假设, contentView
是您想要弹出的视图(包括 TabView)
然后在从该视图导航的任何视图中,您可以调用
extension View {
func popToRoot() {
guard let rootNav = UIApplication.shared.windows.first?.rootViewController as? UINavigationController else { return }
rootNav.popToRootViewController(animated: true)
}
}
// Assume you eventually came to this view.
struct DummyDetailView: View {
var body: some View {
Text("DetailView")
.navigationBarItems(trailing:
Button("Pop to root view") {
self.popToRoot()
}
)
}
}
// EDIT: Requested sample with a viewModel
struct DummyDetailViewWithViewModel: View {
var viewModel: SomeViewModel = SomeViewModel()
var body: some View {
Button("Complete Order!!") {
viewModel.completeOrder(success: { _ in
print("Order Completed")
self.popToRoot()
})
}
}
}
“弹出”到根视图的一种有效方法是在用于导航的NavigationLink
上使用isDetailLink
修饰符。
默认情况下, isDetailLink
为true
。 此修改器用于各种视图容器,例如 iPad,其中详细视图将显示在右侧。
将isDetailLink
设置为false
意味着视图将被推送到NavigationView
堆栈的顶部,也可以被推送出去。
除了在NavigationLink
isDetailLink
设置为 false 外,还将isActive
绑定传递给每个子目标视图。 当您想弹出到根视图时,将值设置为false
它将弹出所有内容:
import SwiftUI
struct ScreenA: View {
@State var isActive : Bool = false
var body: some View {
NavigationView {
NavigationLink(
destination: ScreenB(rootIsActive: self.$isActive),
isActive: self.$isActive
) {
Text("ScreenA")
}
.isDetailLink(false)
.navigationBarTitle("Screen A")
}
}
}
struct ScreenB: View {
@Binding var rootIsActive : Bool
var body: some View {
NavigationLink(destination: ScreenD(shouldPopToRootView: self.$rootIsActive)) {
Text("Next screen")
}
.isDetailLink(false)
.navigationBarTitle("Screen B")
}
}
struct ScreenD: View {
@Binding var shouldPopToRootView : Bool
var body: some View {
VStack {
Text("Last Screen")
Button (action: { self.shouldPopToRootView = false } ){
Text("Pop to root")
}
}.navigationBarTitle("Screen D")
}
}
我已经用self.presentationMode.wrappedValue.dismiss()
解决了这个问题。 这是被调用以返回导航根视图的方法。 这里,TestView 是 ScreenA,ItemList 是 ScreenB,InfoView 是 ScreenC,ItemDetails 是 ScreenD。
import SwiftUI
struct TestView: View {
@State private var currentTab: Tab = .list
var body: some View {
TabView(selection: $currentTab){
ItemList()
.tabItem{
Text("List")
}
.tag(Tab.list)
.navigationBarHidden(false)
InfoView()
.tabItem{
Text("Info")
}
.tag(Tab.info)
.navigationBarHidden(true)
}
}
}
struct ItemList: View {
var body: some View {
VStack{
NavigationView{
List {
NavigationLink(destination: ItemDetails()){
Text("Item")
}
NavigationLink(destination: ItemDetails()){
Text("Item")
}
NavigationLink(destination: ItemDetails()){
Text("Item")
}
NavigationLink(destination: ItemDetails()){
Text("Item")
}
}.navigationBarTitle("Item List")
}
}
}
}
struct InfoView: View {
var body: some View {
Text("This is information view")
}
}
struct ItemDetails: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State private var loading = false
var body: some View {
ZStack {
Text("Connecting...")
.font(.title)
.offset(y: -150)
.pulse(loading: self.loading)
VStack {
Text("This is Item Details")
Button("Connect"){
self.loading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.loading = false
self.presentationMode.wrappedValue.dismiss()
}
}.padding()
}
}
}
}
enum Tab {
case list, info
}
extension View {
func pulse(loading: Bool) -> some View {
self
.opacity(loading ? 1 : 0)
.animation(
Animation.easeInOut(duration: 0.5)
.repeatForever(autoreverses: true)
)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.