iOS SwiftUI:以编程方式弹出或关闭视图

And*_*tto 16 ios swift swiftui

我找不到有关使用SwiftUI 编程方式弹出关闭 所显示视图的任何方式的任何参考。

在我看来,唯一的方法是对模式使用已经集成的幻灯片dow操作(以及是否/如何禁用此功能?),以及对导航堆栈使用后退按钮。

有人知道解决方案吗?您知道这是一个错误还是会一直保持这种状态?

Pra*_*wad 72

SwiftUI Xcode 测试版 5

首先,声明@Environment,它有一个dismiss 方法,你可以在任何地方使用它来关闭视图。

import SwiftUI

struct GameView: View {
    
    @Environment(\.presentationMode) var presentation
    
    var body: some View {
        Button("Done") {
            self.presentation.wrappedValue.dismiss()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 惊人的; 最简单的解决方案。应该在顶部。 (4认同)
  • 适用于 iOS 13、Swift 5。很好的简单解决方案! (3认同)

Chu*_*k H 14

本示例使用Beta 5版本说明中记录的新环境var,该环境使用value属性。在更高的Beta中对其进行了更改,以使用wrappedValue属性。该示例现在是GM版本的最新示例。此完全相同的概念可消除由.sheet修饰符显示的模态视图。

import SwiftUI

struct DetailView: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var body: some View {
        Button(
            "Here is Detail View. Tap to go back.",
            action: { self.presentationMode.wrappedValue.dismiss() }
        )
    }
}

struct RootView: View {
    var body: some View {
        VStack {
            NavigationLink(destination: DetailView())
            { Text("I am Root. Tap for Detail View.") }
        }
    }
}

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

  • 这真是太好了!我只是希望它也适用于双列导航,让我们在用户以纵向模式启动 iPad 时看到分割视图的侧边栏。 (2认同)
  • 这是一个很棒的 iOS 解决方案,我知道这是 OP 的主要目标。但遗憾的是,它似乎不适用于 macOS 导航列表,其中列表和视图同时显示。有什么已知的方法吗? (2认同)

sup*_*cio 8

我最近创建了一个名为swiftui-navigation-stack( https://github.com/biobeats/swiftui-navigation-stack ) 的开源项目,其中包含NavigationStackViewSwiftUI 的替代导航堆栈。它提供了 repo 的自述文件中描述的几个功能。例如,您可以轻松地以编程方式推送和弹出视图。我将通过一个简单的示例向您展示如何做到这一点:

首先,将您的层次结构嵌入到一个NavigationStackVew

struct RootView: View {
    var body: some View {
        NavigationStackView {
            View1()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

NavigationStackView使您的层次结构可以访问一个名为 的有用环境对象NavigationStack。例如,您可以使用它来以编程方式弹出视图,如上述问题中所述:

struct View1: View {
    var body: some View {
        ZStack {
            Color.yellow.edgesIgnoringSafeArea(.all)
            VStack {
                Text("VIEW 1")
                Spacer()

                PushView(destination: View2()) {
                    Text("PUSH TO VIEW 2")
                }
            }
        }
    }
}

struct View2: View {
    @EnvironmentObject var navStack: NavigationStack
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)
            VStack {
                Text("VIEW 2")
                Spacer()

                Button(action: {
                    self.navStack.pop()
                }, label: {
                    Text("PROGRAMMATICALLY POP TO VIEW 1")
                })
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我使用PushView来通过点击来触发推送导航。然后,在View2我使用环境对象以编程方式返回。

这是完整的示例:

import SwiftUI
import NavigationStack

struct RootView: View {
    var body: some View {
        NavigationStackView {
            View1()
        }
    }
}

struct View1: View {
    var body: some View {
        ZStack {
            Color.yellow.edgesIgnoringSafeArea(.all)
            VStack {
                Text("VIEW 1")
                Spacer()

                PushView(destination: View2()) {
                    Text("PUSH TO VIEW 2")
                }
            }
        }
    }
}

struct View2: View {
    @EnvironmentObject var navStack: NavigationStack
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)
            VStack {
                Text("VIEW 2")
                Spacer()

                Button(action: {
                    self.navStack.pop()
                }, label: {
                    Text("PROGRAMMATICALLY POP TO VIEW 1")
                })
            }
        }
    }
}

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

结果是:

在此处输入图片说明

  • 刚刚尝试了一下,它太棒了,它比内置的 SwiftUI 堆栈可靠得多。我递归地将屏幕的 20 个副本推送到堆栈上,内置的副本变得混乱,而您的 NavigationStack 可以完美处理它。 (2认同)

paw*_*222 8

iOS 15

从 iOS 15 开始,我们可以使用一个新的@Environment(\.dismiss)

struct SheetView: View {
    @Environment(\.dismiss) var dismiss

    var body: some View {
        NavigationView {
            Text("Sheet")
                .toolbar {
                    Button("Done") {
                        dismiss()
                    }
                }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(不再需要使用presentationMode.wrappedValue.dismiss().)


有用的链接:

  • 作为 Swift 的相对新手,我根本无法从逻辑上理解这种模式。但它有效,而且我的应用程序将仅适用于 iOS 15,所以谢谢! (5认同)

Dan*_*nar 6

或者,如果您不想通过按钮以编程方式执行此操作,则可以在需要弹出时从视图模型中发出。订阅一个@Published,它会在保存完成时更改值。

struct ContentView: View {
    @ObservedObject var viewModel: ContentViewModel
    @Environment(\.presentationMode) var presentationMode

    init(viewModel: ContentViewModel) {
        self.viewModel = viewModel
    }

    var body: some View {
        Form {
            TextField("Name", text: $viewModel.name)
                .textContentType(.name)
        }
        .onAppear {
            self.viewModel.cancellable = self.viewModel
                .$saved
                .sink(receiveValue: { saved in
                    guard saved else { return }
                    self.presentationMode.wrappedValue.dismiss()
                }
            )
        }
    }
}

class ContentViewModel: ObservableObject {
    @Published var saved = false // This can store any value.
    @Published var name = ""
    var cancellable: AnyCancellable? // You can use a cancellable set if you have multiple observers.

    func onSave() {
        // Do the save.

        // Emit the new value.
        saved = true
    }
}
Run Code Online (Sandbox Code Playgroud)


Sup*_*ers 6

请检查以下代码,非常简单。

第一视角

struct StartUpVC: View {
@State var selection: Int? = nil

var body: some View {
    NavigationView{
        NavigationLink(destination: LoginView().hiddenNavigationBarStyle(), tag: 1, selection: $selection) {
            Button(action: {
                print("Signup tapped")
                self.selection = 1
            }) {
                HStack {
                    Spacer()
                    Text("Sign up")
                    Spacer()
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

第二视角

struct LoginView: View {
@Environment(\.presentationMode) var presentationMode
    
var body: some View {
    NavigationView{
        Button(action: {
           print("Login tapped")
           self.presentationMode.wrappedValue.dismiss()
        }) {
           HStack {
              Image("Back")
              .resizable()
              .frame(width: 20, height: 20)
              .padding(.leading, 20)
           }
        }
      }
   }
}
Run Code Online (Sandbox Code Playgroud)


MSc*_*ler 5

现在,您可以根据需要以编程方式弹出NavigationView。这是beta5。请注意,您不需要后退按钮。您可以通过任何方式以编程方式在DetailView中触发showSelf属性。而且您不必在母版中显示“推送”文本。那可能是EmptyView(),从而创建了一个看不见的序列。

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            MasterView()
        }
    }
}

struct MasterView: View {
    @State var showDetail = false

    var body: some View {
        VStack {
            NavigationLink(destination: DetailView(showSelf: $showDetail), isActive: $showDetail) {
                Text("Push")
            }
        }
    }
}

struct DetailView: View {
    @Binding var showSelf: Bool

    var body: some View {
        Button(action: {
            self.showSelf = false
        }) {
            Text("Pop")
        }
    }
}

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

  • 我的评论的附录。这对我来说是一个巨大的教训:为了能够弹出 2 个或更多,您需要将 `.isDetailLink(false)` 添加到根导航链接。否则,当堆栈中的第三个视图出现时,选择将自动设置为零。 (2认同)