在 SwiftUI 中将绑定传递给导航堆栈中每个视图的最佳方法是啥
Posted
技术标签:
【中文标题】在 SwiftUI 中将绑定传递给导航堆栈中每个视图的最佳方法是啥【英文标题】:What is the best way to pass binding to each view in navigation stack in SwiftUI在 SwiftUI 中将绑定传递给导航堆栈中每个视图的最佳方法是什么 【发布时间】:2021-03-24 13:20:30 【问题描述】:我正在使用 .fullScreenCover 从一个视图转换到另一个视图。呈现的视图有一个导航堆栈,将由几个视图组成。我希望这些视图中的每一个都能够使用 .fullScreenCover 中使用的绑定 isActive 属性来关闭整个堆栈。 我是否需要将此属性绑定到堆栈中的每个视图,或者是否有更简单的方法来执行此操作。我想将此属性添加到导航堆栈的 viewModel 中,但没有成功。这是我想要做的:
struct FirstView: View
@State var isActive = false
var body: some View
Text("Press to present Navigation Stack")
.onTapGesture
isActive = true
.fullScreenCover(isPresented: $isActive, content:
SecondView(isActive: $isActive)
)
struct SecondView: View
@StateObject var viewModel: ViewModel = ViewModel()
@Binding var isActive: Bool
@State var showThirdViewIsActive: Bool = false
init(isActive: Binding<Bool>)
self._isActive = isActive
// somehow get the isActive to my ViewModel but no luck
var body: some View
NavigationView
Button(action:
$showThirdViewIsActive = true
, label:
Text("Show Third View")
)
Button(action:
viewModel.dismiss()
, label:
Text("Dismiss")
)
NavigationLink(
destination: ThirdView(viewModel: viewModel, isActive: $isActive),
isActive: $showThirdViewIsActive)
struct ThirdView: View
@ObservableObject var viewModel: ViewModel
@Binding var isActive: Bool
init(viewModel: ViewModel, isActive: Binding<Bool>)
self.viewModel = viewModel
self._isActive = isActive
// somehow get the isActive to my ViewModel but no luck
var body: some View
Button(action:
viewModel.dismiss()
, label:
Text("dismiss")
)
class ViewModel: ObservableObject
@Binding var isActive: Bool
func dismiss()
self.isActive = false
当然,我可以通过将 isActive 设置为 false 来关闭 SecondView,但我是故意在 ViewModel 中这样做的,因为它不仅适用于这种简单的情况。它可能会在异步调用访问服务器后关闭。
我也刚刚展示了两个视图,但第二个视图中会有 NavigationView,而 NavigationLink 会指向另一个视图,依此类推。它们都将共享相同的 ViewModel。 FirstView 不共享该 ViewModel。
如果可以避免的话,我不想将 isActive 传递给每个视图。我希望能够以某种方式将 isActive 从 SecondView 传递给 viewModel,或者让它成为任何视图都可以访问的某种环境变量。
不确定在我猜测的这种常见情况下的最佳做法是什么。
【问题讨论】:
绑定不是在深层视图层次结构中传递控制的可靠方法,请改用基于 ObservableObject 的视图模型。 是的,但问题是如何将关闭的 isActive 变量放入可观察的 viewModel 【参考方案1】:我不知道你的项目,我认为你在做什么是理所当然的。
如果你想被模型关闭,模型必须在第一个视图中实现。
struct FirstView: View
@StateObject var viewModel: ViewModel = ViewModel()
var body: some View
Text("Press to present Navigation Stack")
.onTapGesture
viewModel.isActive = true
.fullScreenCover(isPresented: $viewModel.isActive, content:
SecondView(viewModel: viewModel)
)
struct SecondView: View
@StateObject var viewModel: ViewModel
@State var showThirdViewIsActive: Bool = false
var body: some View
NavigationView
VStack
Button(action:
showThirdViewIsActive = true
, label:
Text("Show Third View")
)
Button(action:
viewModel.dismiss()
, label:
Text("Dismiss")
)
NavigationLink(
destination: ThirdView(viewModel: viewModel),
isActive: $showThirdViewIsActive)
struct ThirdView: View
@StateObject var viewModel: ViewModel
var body: some View
Button(action:
viewModel.dismiss()
, label:
Text("dismiss")
)
class ViewModel: ObservableObject
@Published var isActive: Bool = false
func dismiss()
self.isActive = false
struct SwiftUIViewTest_Previews: PreviewProvider
static var previews: some View
FirstView()
更新 仅使用@Binding
struct FirstView: View
@State var isActive: Bool = false
var body: some View
Text("Press to present Navigation Stack")
.onTapGesture
isActive = true
.fullScreenCover(isPresented: $isActive, content:
NavigationView
SecondView(isActive: $isActive)
)
.onAppear()
isActive = false
struct SecondView: View
@Binding var isActive: Bool
@State var showThirdViewIsActive: Bool = false
var body: some View
VStack
Button(action:
showThirdViewIsActive = true
, label:
Text("Show Third View")
)
Button(action:
isActive = false
, label:
Text("Dismiss")
)
NavigationLink(
destination: ThirdView(isActive: $isActive),
isActive: $showThirdViewIsActive)
struct ThirdView: View
@Binding var isActive: Bool
var body: some View
Button(action:
isActive = false
, label:
Text("dismiss")
)
【讨论】:
如果我使用这种方法,尽管仅与第二个视图等相关的 ViewModel 会在我不想要的第二个视图被关闭时持续存在。 您可以使用两个模型,一个在第一个视图中移动到另一个视图中,另一个在第二个视图中与您的所有员工一起使用。否则,如果您使用的是 ios 14,则可以使用 @AppStorage @alionthego 通过绑定检查更新以上是关于在 SwiftUI 中将绑定传递给导航堆栈中每个视图的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI 导航链接和@State 在“onAppear()”中有问题