【问题标题】: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

标签: ios swift xcode swiftui


【解决方案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 通过绑定检查更新
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 2021-06-20
  • 1970-01-01
  • 1970-01-01
  • 2021-08-31
  • 2010-10-16
相关资源
最近更新 更多