swiftui - 直接设置选择而不是从 viewModel 设置选择时,SwiftUI NavigationLink 行为不同
问题描述
我一直在使用 SwiftUI 并遇到了意外的行为。
我有视图 A 和视图 B 和视图 C。视图 C 具有从视图 A 更改 AppState 的 EnviromentObject
视图 B 具有带有选择的 ViewModel
如果我从 ViewModel 调用函数来更改选择,则视图 C 会显示几秒钟,然后它会自动弹回视图 B
如果我直接从视图 B(而不是视图模型)更改选择,一切都会按预期工作。另外,如果我注释掉Dissapear,它也可以工作。但是,当屏幕消失时我需要更改 environmentObject 这是 View B 和 ViewModel
import SwiftUI
class AppState: ObservableObject {
@Published
var shouldHideUserInfo = false
}
struct ContentView: View {
@EnvironmentObject
var appState: AppState
@State
var selection: Int? = nil
var body: some View {
NavigationView {
VStack {
if !appState.shouldHideUserInfo {
Text("USER INFO")
}
NavigationLink(
destination: ViewA(),
tag: 1,
selection: $selection,
label: { EmptyView()})
Button("MOVE TO VIEW A") {
selection = 1
}
}
}
}
}
class ViewAModel: ObservableObject {
@Published
var selection: Int? = nil
func navigate() {
selection = 2 //<- this doesnt
}
}
struct ViewA: View {
@ObservedObject
var viewModel: ViewAModel
init() {
viewModel = ViewAModel()
}
@State
var selection: Int? = nil //<- this works
var body: some View {
VStack
{
Text("VIEW A")
NavigationLink(
destination: ViewB(),
tag: 2,
selection: $viewModel.selection,
label: { EmptyView()})
Button("MOVE TO VIEW B") {
//selection = 2 <-- this works
viewModel.navigate() //<- this doesnt
}
}
}
}
struct ViewB: View {
@EnvironmentObject
var appState: AppState
@State
var selection: Int? = nil
var body: some View {
VStack
{
Text("VIEW B")
}
.onAppear {
appState.shouldHideUserInfo = true
}
}
}
工厂模式没有解决问题:
static func makeViewA(param: Int?) -> some View {
let viewModel = ViewAModel(param: param)
return ViewA(viewModel: viewModel)
}
}
解决方案
我明白了……这与帖子中的有点不同。问题是因为视图模型被重新创建(这是长期观察到的行为NavigationView
),因此绑定丢失。
快速修复是
struct ViewA: View {
@StateObject
var viewModel: ViewAModel = ViewAModel()
init() {
// viewModel = ViewAModel()
}
// ... other code
}
替代方法是将视图模型的所有权保留在ViewA
.
更新:兼容 SwiftUI 1.0 - 这是一个适用于任何地方的变体。问题的原因在AppState
. ViewB
更新中的代码appState
.onAppear {
appState.shouldHideUserInfo = true
}
这会导致ContentView
身体重建,重新创建ViewA
,重新创建NavigationLink
,删除先前的链接并ViewB
关闭。
为了防止这种情况,我们需要避免重建ViewA
。这可以通过制作ViewA
is-a来完成Equatable
,因此 SwiftUI 检查是否ViewA
需要重新创建,我们将回答 NO。
这是怎么回事:
NavigationLink(
destination: ViewA().equatable(), // << here !!
tag: 1,
selection: $selection,
label: { EmptyView()})
和
struct ViewA: View, Equatable {
static func == (lhs: ViewA, rhs: ViewA) -> Bool {
true
}
// .. other code
推荐阅读
- homebrew - 使用 Homebrew 安装 applesimutils 的问题
- pandas - 是否有等效于 numpy.digitize 的适用于 pandas.IntervalIndex?
- javascript - reactjs onclick里面的地图
- java - 将文件名添加到 Path 对象
- mongodb - 如何使用聚合框架只选择具有子字段的 MongoDB 文档?
- javascript - 如何通过 [ngClass] 清除或删除 Angular 6 中的类
- php - 将所有 MYSQL 结果放入变量中以在 Ajax 中使用
- amazon-web-services - 升级 EC2 实例时,AWS 内部会发生什么
- c# - 实体框架 - 数据读取器与指定的复杂类型不兼容
- javascript - Webpack 开发服务器错误:不变违规:最小化 React 错误 #200