[英]How to navigate from a subview of a TabView back to the TabView in SwiftUI?
In SwiftUI a TabView must be the root view.在 SwiftUI 中,TabView 必须是根视图。 You, therefore, cannot use a NavigationLink to navigate to a TabView.因此,您不能使用 NavigationLink 导航到 TabView。 So say for instance I have four screens in my app.例如,假设我的应用程序中有四个屏幕。
Screen A is a TabView that holds Screen B and Screen C.屏幕 A 是一个 TabView,包含屏幕 B 和屏幕 C。 Screen B is a List that has a NavigationLink to take you to a list item details (Screen D) Screen C is an information view (it's not important in the question) Screen D is a list item details screen, you must first navigate to screen b to get here.屏幕 B 是具有 NavigationLink 的列表,可将您带到列表项详细信息(屏幕 D)屏幕 C 是信息视图(在问题中并不重要)屏幕 D 是列表项详细信息屏幕,您必须先导航到屏幕b 到这里。 Screen D, however, has a button that should perform a network action in a ViewModel, and then take you to ScreenA upon completion.然而,屏幕 D 有一个按钮,应该在 ViewModel 中执行网络操作,然后在完成后将您带到屏幕 A。
How can Screen D navigation back two levels to the root screen (Screen A)?屏幕 D 如何导航回两个级别到根屏幕(屏幕 A)?
I did a trick like this, worked for me.我做了这样的把戏,为我工作。
In SceneDelegate.swift, I modified auto generated code.在 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()
}
Note : I expected embedding TabView
inside NavigationView
would do the same job, but did not work, it was the reason of doing this trick.注意:我希望在NavigationView
中嵌入TabView
会做同样的工作,但没有奏效,这是做这个技巧的原因。
I assume, contentView
is the View that you want to pop to (the one includes TabView)我假设, contentView
是您想要弹出的视图(包括 TabView)
Then in any view navigated from that view, you can call然后在从该视图导航的任何视图中,您可以调用
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()
})
}
}
}
An effective way to "pop" to your root view would be to utilize the isDetailLink
modifier on the NavigationLink
that is used for navigating. “弹出”到根视图的一种有效方法是在用于导航的NavigationLink
上使用isDetailLink
修饰符。
By default, isDetailLink
is true
.默认情况下, isDetailLink
为true
。 This modifier is used for a variety of view containers, like on iPad, where the detail view would appear on the right side.此修改器用于各种视图容器,例如 iPad,其中详细视图将显示在右侧。
Setting isDetailLink
to false
means that the view will be pushed on top of the NavigationView
stack, and can also be pushed off.将isDetailLink
设置为false
意味着视图将被推送到NavigationView
堆栈的顶部,也可以被推送出去。
Along with setting isDetailLink
to false on NavigationLink
, pass the isActive
binding to each child destination view.除了在NavigationLink
isDetailLink
设置为 false 外,还将isActive
绑定传递给每个子目标视图。 When you want to pop to the root view, set the value to false
and it will pop everything off:当您想弹出到根视图时,将值设置为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")
}
}
I have resolved this with self.presentationMode.wrappedValue.dismiss()
.我已经用self.presentationMode.wrappedValue.dismiss()
解决了这个问题。 This is the method that is called to bring back to View of Navigation Root.这是被调用以返回导航根视图的方法。 Here, TestView is ScreenA, ItemList is ScreenB, InfoView is ScreenC and ItemDetails is ScreenD.这里,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.