SwiftUI:如何弹出到根视图

Chu*_*k H 14 swift swiftui

最后,现在有了Beta 5,我们可以以编程方式弹出到父视图。但是,在我的应用程序中,有几个地方视图都有一个“保存”按钮,该按钮可以结束几个步骤并返回到开始。在UIKit中,我使用popToRootViewController(),但是我一直无法找到在SwiftUI中执行相同操作的方法。

以下是我尝试实现的模式的简单示例。有任何想法吗?

import SwiftUI

struct DetailViewB: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var body: some View {
        VStack {
            Text("This is Detail View B.")

            Button(action: { self.presentationMode.value.dismiss() } )
            { Text("Pop to Detail View A.") }

            Button(action: { /* How to do equivalent to popToRootViewController() here?? */ } )
            { Text("Pop two levels to Master View.") }

        }
    }
}

struct DetailViewA: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var body: some View {
        VStack {
            Text("This is Detail View A.")

            NavigationLink(destination: DetailViewB() )
            { Text("Push to Detail View B.") }

            Button(action: { self.presentationMode.value.dismiss() } )
            { Text("Pop one level to Master.") }
        }
    }
}

struct MasterView: View {
    var body: some View {
        VStack {
            Text("This is Master View.")

            NavigationLink(destination: DetailViewA() )
            { Text("Push to Detail View A.") }
        }
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            MasterView()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

mal*_*hal 155

将视图修饰符设置isDetailLinkfalseon aNavigationLink是让 pop-to-root 工作的关键。isDetailLinktrue默认的并且适应包含视图。例如,在 iPad 横向上,拆分视图是分开的,并isDetailLink确保目标视图将显示在右侧。因此设置isDetailLinkfalse意味着目标视图将始终被推送到导航堆栈上;因此总是可以弹出。

随着设置isDetailLinkfalseon NavigationLink,将isActive绑定传递到每个后续目标视图。最后当你想弹出到根视图时,将值设置为false它会自动弹出所有内容:

import SwiftUI

struct ContentView: View {
    @State var isActive : Bool = false

    var body: some View {
        NavigationView {
            NavigationLink(
                destination: ContentView2(rootIsActive: self.$isActive),
                isActive: self.$isActive
            ) {
                Text("Hello, World!")
            }
            .isDetailLink(false)
            .navigationBarTitle("Root")
        }
    }
}

struct ContentView2: View {
    @Binding var rootIsActive : Bool

    var body: some View {
        NavigationLink(destination: ContentView3(shouldPopToRootView: self.$rootIsActive)) {
            Text("Hello, World #2!")
        }
        .isDetailLink(false)
        .navigationBarTitle("Two")
    }
}

struct ContentView3: View {
    @Binding var shouldPopToRootView : Bool

    var body: some View {
        VStack {
            Text("Hello, World #3!")
            Button (action: { self.shouldPopToRootView = false } ){
                Text("Pop to root")
            }
        }.navigationBarTitle("Three")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Run Code Online (Sandbox Code Playgroud)

屏幕截图

  • 它有效,但“shouldPopToRootView”的命名不清楚。该属性有效地禁用了根视图上的导航。此外,最好使用环境对象来观察变化,而不是将该绑定布尔值传递给子视图中的每个视图。 (12认同)
  • 这是最好的答案,现在应该是公认的答案。它完全符合我的要求,而且不是黑客。谢谢。 (10认同)
  • 对于那些在视图上使用自定义初始值设定项但无法使其正常工作的用户,请确保在初始化参数“init(rootIsActive: Binding&lt;Bool&gt;)”上使用 Binding&lt;Type&gt; ,也在初始值设定项内不要忘记使用下划线表示本地绑定变量 (self._rootIsActive = rootIsActive)。当您的预览停止时,只需使用 .constant(true) 作为参数。 (7认同)
  • 感谢您的灵感和代码。我的两个关键点: - ContentView 中不需要指令 .isDetailLink(false) (因为它是根视图)。- 布尔值 rootIsActive 和 shouldPopToRootView 的命名非常非常糟糕。由于它们,我在理解代码时遇到了很多困难。尤其是 self.shouldPopToRootView = false 的东西看起来怪异地向后(假...?真的...?我们实际上是在尝试弹出到根视图,你知道...)。我所做的就是将它们(与 ContentView 中的 isActive 一起)替换为一个名为 stackingPermission 的布尔值。 (4认同)
  • 我很难看出 `self.shouldPopToRootView = false` 实现了什么?看起来我们只是将 false 从视图 1 传递到视图 3。 (4认同)
  • 如果根视图中有多个导航链接,那么此解决方案可能会有点棘手。不要只为所有导航链接(在根视图中)向 isActive 提供相同的布尔绑定。否则,当您导航时,所有导航链接将同时变为活动状态。棘手。 (3认同)

Chu*_*k H 30

当然,@malhal 是解决方案的关键,但对我来说,将 Binding 作为参数传递到 View 中是不切实际的。正如@Imthath 所指出的,环境是一种更好的方式。

这是模仿 Apple 发布的dismiss() 方法以弹出到上一个视图的另一种方法。

定义环境的扩展:

struct RootPresentationModeKey: EnvironmentKey {
    static let defaultValue: Binding<RootPresentationMode> = .constant(RootPresentationMode())
}

extension EnvironmentValues {
    var rootPresentationMode: Binding<RootPresentationMode> {
        get { return self[RootPresentationModeKey.self] }
        set { self[RootPresentationModeKey.self] = newValue }
    }
}

typealias RootPresentationMode = Bool

extension RootPresentationMode {
    
    public mutating func dismiss() {
        self.toggle()
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

  1. 添加.environment(\.rootPresentationMode, self.$isPresented)到根NavigationView,其中isPresentedBool用于呈现所述第一子图。

  2. .navigationViewStyle(StackNavigationViewStyle())修饰符添加到 root NavigationView,或添加.isDetailLink(false)NavigationLink第一个子视图。

  3. @Environment(\.rootPresentationMode) private var rootPresentationMode从应该执行 pop 到 root 的位置添加到任何子视图。

  4. 最后,self.rootPresentationMode.wrappedValue.dismiss()从该子视图调用将弹出到根视图。

我在 GitHub 上发布了一个完整的工作示例:

https://github.com/Whiffer/SwiftUI-PopToRootExample

  • 如何与 TabView 和多个不同的“根”屏幕一起使用? (4认同)
  • 这确实对我有帮助。谢谢查克和尼古拉。 (2认同)

mic*_*cah 14

iOS 16 解决方案

现在终于可以用新添加的内容弹出到根视图了NavigationStack

struct DataObject: Identifiable, Hashable {
    let id = UUID()
    let name: String
}

@available(iOS 16.0, *)
struct ContentView8: View {
    @State private var path = NavigationPath()
    
    var body: some View {
        NavigationStack(path: $path) {
            Text("Root Pop")
                .font(.largeTitle)
                .foregroundColor(.primary)
            
            NavigationLink("Click Item", value: DataObject.init(name: "Item"))
            
            .listStyle(.plain)
            .navigationDestination(for: DataObject.self) { course in
                Text(course.name)
                NavigationLink("Go Deeper", value: DataObject.init(name: "Item"))
                Button("Back to root") {
                    path = NavigationPath()
                }
            }
        }
        .padding()
    }
}
Run Code Online (Sandbox Code Playgroud)


Sup*_*oob 12

女士们,先生们,介绍 Apple 对这个问题的解决方案。 *也通过 HackingWithSwift 呈现给你(我从 lol 那里偷了这个):在程序化导航下

(在 Xcode 12 和 iOS 14 上测试)

基本上你使用tagand selectioninsidenavigationlink直接进入你想要的任何页面。

struct ContentView: View {
@State private var selection: String? = nil

var body: some View {
    NavigationView {
        VStack {
            NavigationLink(destination: Text("Second View"), tag: "Second", selection: $selection) { EmptyView() }
            NavigationLink(destination: Text("Third View"), tag: "Third", selection: $selection) { EmptyView() }
            Button("Tap to show second") {
                self.selection = "Second"
            }
            Button("Tap to show third") {
                self.selection = "Third"
            }
        }
        .navigationBarTitle("Navigation")
    }
}
}
Run Code Online (Sandbox Code Playgroud)

您可以使用@environmentobject注入ContentView()来处理选择:

class NavigationHelper: ObservableObject {
    @Published var selection: String? = nil
}
Run Code Online (Sandbox Code Playgroud)

注入应用程序:

@main
struct YourApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(NavigationHelper())
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用它:

struct ContentView: View {
@EnvironmentObject var navigationHelper: NavigationHelper

var body: some View {
    NavigationView {
        VStack {
            NavigationLink(destination: Text("Second View"), tag: "Second", selection: $navigationHelper.selection) { EmptyView() }
            NavigationLink(destination: Text("Third View"), tag: "Third", selection: $navigationHelper.selection) { EmptyView() }
            Button("Tap to show second") {
                self.navigationHelper.selection = "Second"
            }
            Button("Tap to show third") {
                self.navigationHelper.selection = "Third"
            }
        }
        .navigationBarTitle("Navigation")
    }
}
}
Run Code Online (Sandbox Code Playgroud)

要返回子导航链接中的内容视图,您只需设置navigationHelper.selection = nil.

请注意,如果您不想,您甚至不必为后续的子导航链接使用标记和选择——尽管它们没有转到特定导航链接的功能。


小智 10

由于目前 SwiftUI 仍然在后台使用 UINavigationController,因此也可以调用它的popToRootViewController(animated:)函数。你只需要像这样搜索 UINavigationController 的视图控制器层次结构:

struct NavigationUtil {
  static func popToRootView() {
    findNavigationController(viewController: UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.rootViewController)?
      .popToRootViewController(animated: true)
  }

  static func findNavigationController(viewController: UIViewController?) -> UINavigationController? {
    guard let viewController = viewController else {
      return nil
    }

    if let navigationController = viewController as? UINavigationController {
      return navigationController
    }

    for childViewController in viewController.children {
      return findNavigationController(viewController: childViewController)
    }

    return nil
  }
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

struct ContentView: View {
    var body: some View {
      NavigationView { DummyView(number: 1) }
    }
}

struct DummyView: View {
  let number: Int

  var body: some View {
    VStack(spacing: 10) {
      Text("This is view \(number)")
      NavigationLink(destination: DummyView(number: number + 1)) {
        Text("Go to view \(number + 1)")
      }
      Button(action: { NavigationUtil.popToRootView() }) {
        Text("Or go to root view!")
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这似乎只适用于带有 NavigationView 的一个视图。如果您有一个带有 NavigationView 的多个视图的 TabView,则它仅适用于第一个视图 (8认同)

小智 9

我想出了一个简单的解决方案来弹出根视图。我正在发送通知,然后监听通知以更改 NavigationView 的 id;这将刷新导航视图。虽然没有动画,但是看起来不错。这是示例:

@main
struct SampleApp: App {
    @State private var navigationId = UUID()

    var body: some Scene {
        WindowGroup {
            NavigationView {
                Screen1()
            }
            .id(navigationId)
            .onReceive(NotificationCenter.default.publisher(for: Notification.Name("popToRootView"))) { output in
                navigationId = UUID()
            }
        }
    }
}

struct Screen1: View {
    var body: some View {
        VStack {
            Text("This is screen 1")
            NavigationLink("Show Screen 2", destination: Screen2())
        }
    }
}

struct Screen2: View {
    var body: some View {
        VStack {
            Text("This is screen 2")
            Button("Go to Home") {
                NotificationCenter.default.post(name: Notification.Name("popToRootView"), object: nil)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 7

这是对x0randgat3 答案的更新,适用NavigationViewsTabView.

struct NavigationUtil {
    static func popToRootView(animated: Bool = false) {
        findNavigationController(viewController: UIApplication.shared.connectedScenes.flatMap { ($0 as? UIWindowScene)?.windows ?? [] }.first { $0.isKeyWindow }?.rootViewController)?.popToRootViewController(animated: animated)
    }
    
    static func findNavigationController(viewController: UIViewController?) -> UINavigationController? {
        guard let viewController = viewController else {
            return nil
        }
        
        if let navigationController = viewController as? UITabBarController {
            return findNavigationController(viewController: navigationController.selectedViewController)
        }
        
        if let navigationController = viewController as? UINavigationController {
            return navigationController
        }
        
        for childViewController in viewController.children {
            return findNavigationController(viewController: childViewController)
        }
        
        return nil
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 对我有用。除了“windows”在 iOS 15.0 中已被弃用。通过更新以下内容修复: findNavigationController(viewController: UIApplication.shared.connectedScenes.flatMap { ($0 as? UIWindowScene)?.windows ?? [] }.first { $0.isKeyWindow }?.rootViewController)? (3认同)

Fab*_*yne 5

我花了最后几个小时来尝试解决相同的问题。据我所知,使用当前的beta 5并没有简单的方法。我发现的唯一方法是非常hacky,但是可以使用。基本上将发布者添加到您的DetailViewA中,这将从DetailViewB触发。在DetailViewB中,关闭视图并通知发布者,他自己将关闭DetailViewA。

    struct DetailViewB: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var publisher = PassthroughSubject<Void, Never>()

    var body: some View {
        VStack {
            Text("This is Detail View B.")

            Button(action: { self.presentationMode.value.dismiss() } )
            { Text("Pop to Detail View A.") }

            Button(action: {
                DispatchQueue.main.async {
                self.presentationMode.wrappedValue.dismiss()
                self.publisher.send()
                }
            } )
            { Text("Pop two levels to Master View.") }

        }
    }
}

struct DetailViewA: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var publisher = PassthroughSubject<Void, Never>()

    var body: some View {
        VStack {
            Text("This is Detail View A.")

            NavigationLink(destination: DetailViewB(publisher:self.publisher) )
            { Text("Push to Detail View B.") }

            Button(action: { self.presentationMode.value.dismiss() } )
            { Text("Pop one level to Master.") }
        }
        .onReceive(publisher, perform: { _ in
            DispatchQueue.main.async {
                print("Go Back to Master")
                self.presentationMode.wrappedValue.dismiss()
            }
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

[更新]我仍在努力,因为上一个Beta 6仍没有解决方案。

我找到了另一种方法可以返回到根目录,但是这次我丢失了动画,直接进入了根目录。想法是强制刷新根视图,以这种方式导致导航堆栈的清理。

但是最终,只有Apple才能带来适当的解决方案,因为SwiftUI中无法使用导航堆栈的管理。

注意:以下通知提供的简单解决方案适用于iOS而非watchOS,因为watchOS会在2个导航级别后从内存中清除根视图。但是让外部类管理watchOS的状态应该可以正常工作。

struct DetailViewB: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

    @State var fullDissmiss:Bool = false
    var body: some View {
        SGNavigationChildsView(fullDissmiss: self.fullDissmiss){
            VStack {
                Text("This is Detail View B.")

                Button(action: { self.presentationMode.wrappedValue.dismiss() } )
                { Text("Pop to Detail View A.") }

                Button(action: {
                    self.fullDissmiss = true
                } )
                { Text("Pop two levels to Master View with SGGoToRoot.") }
            }
        }
    }
}

struct DetailViewA: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

    @State var fullDissmiss:Bool = false
    var body: some View {
        SGNavigationChildsView(fullDissmiss: self.fullDissmiss){
            VStack {
                Text("This is Detail View A.")

                NavigationLink(destination: DetailViewB() )
                { Text("Push to Detail View B.") }

                Button(action: { self.presentationMode.wrappedValue.dismiss() } )
                { Text("Pop one level to Master.") }

                Button(action: { self.fullDissmiss = true } )
                { Text("Pop one level to Master with SGGoToRoot.") }
            }
        }
    }
}

struct MasterView: View {
    var body: some View {
        VStack {
            Text("This is Master View.")
            NavigationLink(destination: DetailViewA() )
            { Text("Push to Detail View A.") }
        }
    }
}

struct ContentView: View {

    var body: some View {
        SGRootNavigationView{
            MasterView()
        }
    }
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

struct SGRootNavigationView<Content>: View where Content: View {
    let cancellable = NotificationCenter.default.publisher(for: Notification.Name("SGGoToRoot"), object: nil)

    let content: () -> Content

    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }

    @State var goToRoot:Bool = false

    var body: some View {
        return
            Group{
            if goToRoot == false{
                NavigationView {
                content()
                }
            }else{
                NavigationView {
                content()
                }
            }
            }.onReceive(cancellable, perform: {_ in
                DispatchQueue.main.async {
                    self.goToRoot.toggle()
                }
            })
    }
}

struct SGNavigationChildsView<Content>: View where Content: View {
    let notification = Notification(name: Notification.Name("SGGoToRoot"))

    var fullDissmiss:Bool{
        get{ return false }
        set{ if newValue {self.goToRoot()} }
    }

    let content: () -> Content

    init(fullDissmiss:Bool, @ViewBuilder content: @escaping () -> Content) {
        self.content = content
        self.fullDissmiss = fullDissmiss
    }

    var body: some View {
        return Group{
            content()
        }
    }

    func goToRoot(){
        NotificationCenter.default.post(self.notification)
    }
}
Run Code Online (Sandbox Code Playgroud)


kpr*_*ter 5

花了一些时间,但我想出了如何在 swiftui 中使用复杂的导航。诀窍是收集视图的所有状态,以判断它们是否显示。

首先定义一个 NavigationController。我添加了 tabview 选项卡的选择和布尔值,表示是否显示特定视图

import SwiftUI
final class NavigationController: ObservableObject  {

  @Published var selection: Int = 1

  @Published var tab1Detail1IsShown = false
  @Published var tab1Detail2IsShown = false

  @Published var tab2Detail1IsShown = false
  @Published var tab2Detail2IsShown = false
}
Run Code Online (Sandbox Code Playgroud)

使用两个选项卡设置 tabview 并将我们的 NavigationController.selection 绑定到 tabview:

import SwiftUI
struct ContentView: View {

  @EnvironmentObject var nav: NavigationController

  var body: some View {

    TabView(selection: self.$nav.selection){

            FirstMasterView() 
            .tabItem {
                 Text("First")
            }
            .tag(0)

           SecondMasterView() 
            .tabItem {
                 Text("Second")
            }
            .tag(1)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

例如,这是一个 navigationStacks

import SwiftUI


struct FirstMasterView: View {

    @EnvironmentObject var nav: NavigationController

   var body: some View {
      NavigationView{
        VStack{

          NavigationLink(destination: FirstDetailView(), isActive: self.$nav.tab1Detail1IsShown) {
                Text("go to first detail")
            }
        } .navigationBarTitle(Text("First MasterView"))
     }
  }
}

struct FirstDetailView: View {

   @EnvironmentObject var nav: NavigationController
   @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

 var body: some View {

    VStack(spacing: 20) {
        Text("first detail View").font(.title)


        NavigationLink(destination: FirstTabLastView(), isActive: self.$nav.tab1Detail2IsShown) {
            Text("go to last detail on nav stack")
        }

        Button(action: {
            self.nav.tab2Detail1IsShown = false //true will go directly to detail
            self.nav.tab2Detail2IsShown = false 

            self.nav.selection = 1
        }) { Text("Go to second tab")
        }
    }
        //in case of collapsing all the way back
        //there is a bug with the environment object
        //to go all the way back I have to use the presentationMode
        .onReceive(self.nav.$tab1Detail2IsShown, perform: { (out) in
            if out ==  false {
                 self.presentationMode.wrappedValue.dismiss()
            }
        })
    }
 }


struct FirstTabLastView: View {
   @EnvironmentObject var nav: NavigationController

   var body: some View {
       Button(action: {
           self.nav.tab1Detail1IsShown = false
           self.nav.tab1Detail2IsShown = false
       }) {Text("Done and go back to beginning of navigation stack")
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

我希望我能解释这种方法,它非常面向 SwiftUI 状态。

  • 如果您想使用列表,方法非常相似。我不会将 NavigationLink 放入列表中(因为这会创建不同的链接,正如您所提到的)。您可以添加编程链接(意味着您没有可见的按钮)。NavigationLink(目的地:MyView(数据:mySelectedDataFromTheList),isActive:$ self.nav.isShown){EmptyView()}。当用户在列表中的某个项目上单击选项卡时,您可以将 mySelectedDataFromTheList 设置为选项卡式项目并将导航状态 isShown 更改为 true。 (2认同)

Mic*_*nry 5

对我来说,为了实现对 swiftUI 中仍然缺少的导航的完全控制,我只是将 SwiftUI 视图嵌入到UINavigationController. 里面SceneDelegate。请注意,我隐藏了导航栏以便使用 NavigationView 作为我的显示。

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

        UINavigationBar.appearance().tintColor = .black

        let contentView = OnBoardingView()
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            let hostingVC = UIHostingController(rootView: contentView)
            let mainNavVC = UINavigationController(rootViewController: hostingVC)
            mainNavVC.navigationBar.isHidden = true
            window.rootViewController = mainNavVC
            self.window = window
            window.makeKeyAndVisible()
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

然后我创建了这个协议和扩展, HasRootNavigationController

import SwiftUI
import UIKit

protocol HasRootNavigationController {
    var rootVC:UINavigationController? { get }

    func push<Content:View>(view: Content, animated:Bool)
    func setRootNavigation<Content:View>(views:[Content], animated:Bool)
    func pop(animated: Bool)
    func popToRoot(animated: Bool)
}

extension HasRootNavigationController where Self:View {

    var rootVC:UINavigationController? {
        guard let scene = UIApplication.shared.connectedScenes.first,
            let sceneDelegate = scene as? UIWindowScene,
            let rootvc = sceneDelegate.windows.first?.rootViewController
                as? UINavigationController else { return nil }
        return rootvc
    }

    func push<Content:View>(view: Content, animated:Bool = true) {
        rootVC?.pushViewController(UIHostingController(rootView: view), animated: animated)
    }

    func setRootNavigation<Content:View>(views: [Content], animated:Bool = true) {
        let controllers =  views.compactMap { UIHostingController(rootView: $0) }
        rootVC?.setViewControllers(controllers, animated: animated)
    }

    func pop(animated:Bool = true) {
        rootVC?.popViewController(animated: animated)
    }

    func popToRoot(animated: Bool = true) {
        rootVC?.popToRootViewController(animated: animated)
    }
}

Run Code Online (Sandbox Code Playgroud)

之后在我的 SwiftUI 视图上我使用/实现了HasRootNavigationController协议和扩展

extension YouSwiftUIView:HasRootNavigationController {

    func switchToMainScreen() {
        self.setRootNavigation(views: [MainView()])
    }

    func pushToMainScreen() {
         self.push(view: [MainView()])
    }

    func goBack() {
         self.pop()
    }

    func showTheInitialView() {
         self.popToRoot()
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的代码的要点,以防我有一些更新。https://gist.github.com/michaelhenry/945fc63da49e960953b72bbc567458e6


squ*_*o10 5

该解决方案基于malhal 的回答,使用ImthathFlorin Odagiu的建议,并需要 Paul Hudson 的 NavigationView 视频为我将所有内容整合在一起。

这个想法很简单。点击时,navigationLink 的 isActive 参数设置为 true。这允许出现第二个视图。您可以使用其他链接来添加更多视图。要返回到根目录,只需将 isActive 设置为 false 即可。第二个视图以及任何其他可能堆积起来的视图都会消失。

import SwiftUI

class Views: ObservableObject {
    @Published var stacked = false
}

struct ContentView: View {
    @ObservedObject var views = Views()

    var body: some View {
        NavigationView {
            NavigationLink(destination: ContentView2(), isActive: self.$views.stacked) {
                Text("Go to View 2") // Tapping this link sets stacked to true
            }
            .isDetailLink(false)
            .navigationBarTitle("ContentView")
        }
        .environmentObject(views) // Inject a new views instance into the navigation view environment so that it's available to all views presented by the navigation view.
    }
}

struct ContentView2: View {

    var body: some View {
        NavigationLink(destination: ContentView3()) {
            Text("Go to View 3")
        }
        .isDetailLink(false)
        .navigationBarTitle("View 2")
    }
}

struct ContentView3: View {
    @EnvironmentObject var views: Views

    var body: some View {

        Button("Pop to root") {
            self.views.stacked = false // By setting this to false, the second view that was active is no more. Which means, the content view is being shown once again.
        }
        .navigationBarTitle("View 3")
    }
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

1957 次

最近记录:

5 年,11 月 前