从 NavigationView 关闭 SwiftUI 中的父模式

raf*_*nes 8 ios swiftui

我知道如何使用从子视图中关闭模态,@Environment (\.presentationMode) var presentationMode / self.presentationMode.wrappedValue.dismiss()但这是一个不同的问题。

当您NavigationView在模态窗口中显示多页并浏览了几个页面时,presentationMode对 NavigationView的引用将更改为 NavigationView,因此使用self.presentationMode.wrappedValue.dismiss()简单地弹出最后一个 NavigationView 而不是解除包含的模态。

是否有可能 - 如果有的话 - 如何从 NavigationView 树中的页面关闭包含模式?

这是一个显示问题的简单示例。如果您使用 SwiftUI 创建一个 Xcode Single View 应用程序项目并用ContentView它替换默认代码,它应该无需进一步更改即可工作。

import SwiftUI

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

  var body: some View {
    Button(action: {
      self.showModal.toggle()
    }) {
      Text("Launch Modal")
    }
    .sheet(isPresented: self.$showModal, onDismiss: {
      self.showModal = false
    }) {
      PageOneContent()
    }
  }
}

struct PageOneContent: View {
  var body: some View {
    NavigationView {
      VStack {
        Text("I am Page One")
      }
      .navigationBarTitle("Page One")
      .navigationBarItems(
        trailing: NavigationLink(destination: PageTwoContent()) {
          Text("Next")
        })
      }
  }
}

struct PageTwoContent: View {

  @Environment (\.presentationMode) var presentationMode

  var body: some View {
    NavigationView {
      VStack {
        Text("This should dismiss the modal. But it just pops the NavigationView")
          .padding()

        Button(action: {
          // How to dismiss parent modal here instead
          self.presentationMode.wrappedValue.dismiss()
        }) {
          Text("Finish")
        }
        .padding()
        .foregroundColor(.white)
        .background(Color.blue)
      }
      .navigationBarTitle("Page Two")
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Asp*_*eri 9

这是基于使用自己明确创建的环境密钥的可能方法(实际上我觉得presentationMode用于这个用例是不正确的......无论如何)。

提议的方法是通用的,适用于模态视图层次结构中的任何视图。已测试并适用于 Xcode 11.2 / iOS 13.2。

// define env key to store our modal mode values
struct ModalModeKey: EnvironmentKey {
    static let defaultValue = Binding<Bool>.constant(false) // < required
}

// define modalMode value
extension EnvironmentValues {
    var modalMode: Binding<Bool> {
        get {
            return self[ModalModeKey.self]
        }
        set {
            self[ModalModeKey.self] = newValue
        }
    }
}


struct ParentModalTest: View {
  @State var showModal: Bool = false

  var body: some View {
    Button(action: {
      self.showModal.toggle()
    }) {
      Text("Launch Modal")
    }
    .sheet(isPresented: self.$showModal, onDismiss: {
    }) {
      PageOneContent()
        .environment(\.modalMode, self.$showModal) // < bind modalMode
    }
  }
}

struct PageOneContent: View {
  var body: some View {
    NavigationView {
      VStack {
        Text("I am Page One")
      }
      .navigationBarTitle("Page One")
      .navigationBarItems(
        trailing: NavigationLink(destination: PageTwoContent()) {
          Text("Next")
        })
      }
  }
}

struct PageTwoContent: View {

  @Environment (\.modalMode) var modalMode // << extract modalMode

  var body: some View {
    NavigationView {
      VStack {
        Text("This should dismiss the modal. But it just pops the NavigationView")
          .padding()

        Button(action: {
          self.modalMode.wrappedValue = false // << close modal
        }) {
          Text("Finish")
        }
        .padding()
        .foregroundColor(.white)
        .background(Color.blue)
      }
      .navigationBarTitle("Page Two")
    }
  }
}
Run Code Online (Sandbox Code Playgroud)


Seb*_*ldt 6

另一种方法是简单地使用这种情况的通知,并重置模态的触发标志。对我来说这不是最漂亮的解决方案,但它是我最有可能在几个月后仍然理解的解决方案。

import SwiftUI

struct ContentView: View {
    @State var showModalNav: Bool = false
    
    var body: some View {
        Text("Present Modal")
            .padding()
            .onTapGesture {
                showModalNav.toggle()
            }.sheet(isPresented: $showModalNav, content: {
                ModalNavView()
            }).onReceive(NotificationCenter.default.publisher(for: Notification.Name(rawValue: "PushedViewNotifciation"))) { _ in
                showModalNav = false
            }
    }
}


struct ModalNavView: View {
    var body: some View {
        NavigationView {
            NavigationLink(
                destination: PushedView(),
                label: {
                    Text("Show Another View")
                }
            )
        }
    }
}

struct PushedView: View {
    var body: some View {
        Text("Pushed View").onTapGesture {
            NotificationCenter.default.post(Notification.init(name: Notification.Name(rawValue: "PushedViewNotifciation")))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不想通过通知松散地耦合视图,您也可以使用绑定,如下所示:

struct ContentView: View {
    @State var showModalNav: Bool = false
    
    var body: some View {
        Text("Present Modal")
            .padding()
            .onTapGesture {
                showModalNav.toggle()
            }.sheet(isPresented: $showModalNav, content: {
                ModalNavView(parentShowModal: $showModalNav)
            }).onReceive(NotificationCenter.default.publisher(for: Notification.Name(rawValue: "PushedViewNotifciation"))) { _ in
                showModalNav = false
            }
    }
}


struct ModalNavView: View {
    @Binding var parentShowModal: Bool
    var body: some View {
        NavigationView {
            NavigationLink(
                destination: PushedView(parentShowModal: $parentShowModal),
                label: {
                    Text("Show Another View")
                }
            )
        }
    }
}

struct PushedView: View {
    @Binding var parentShowModal: Bool
    var body: some View {
        Text("Pushed View").onTapGesture {
            parentShowModal = false
        }
    }
}
Run Code Online (Sandbox Code Playgroud)