直接设置选择而不是从 viewModel 设置选择时,SwiftUI NavigationLink 行为不同
SwiftUI NavigationLink behaviour is different when selection is set directly, rather than from viewModel
我一直在使用 SwiftUI 和 运行 遇到意外行为。
我有视图 A、视图 B 和视图 C。视图 C 具有从视图 A 更改 AppState 的 EnviromentObject
视图 B 有带选择的 ViewModel
如果我从 ViewModel 调用函数来更改选择,那么
视图 C 显示几秒钟,然后自动弹出回到视图 B
如果我直接从视图 B(而不是 ViewModel)更改选择,一切都会按预期进行。
另外,如果我注释掉 onDissapear,它也有效。但是,我需要在屏幕消失时更改 environmentObject
这是视图 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)
}
}
我明白了...它与 post 中的有点不同。问题是因为重新创建了视图模型(这是 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
,丢弃之前的 link 和 ViewB
关闭。
为防止这种情况,我们需要避免重建 ViewA
。这可以通过使 ViewA
is-a Equatable
来完成,因此 SwiftUI 检查是否需要重新创建 ViewA
,我们将回答否。
事情是这样的:
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
我一直在使用 SwiftUI 和 运行 遇到意外行为。
我有视图 A、视图 B 和视图 C。视图 C 具有从视图 A 更改 AppState 的 EnviromentObject
视图 B 有带选择的 ViewModel
如果我从 ViewModel 调用函数来更改选择,那么 视图 C 显示几秒钟,然后自动弹出回到视图 B
如果我直接从视图 B(而不是 ViewModel)更改选择,一切都会按预期进行。 另外,如果我注释掉 onDissapear,它也有效。但是,我需要在屏幕消失时更改 environmentObject 这是视图 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)
}
}
我明白了...它与 post 中的有点不同。问题是因为重新创建了视图模型(这是 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
,丢弃之前的 link 和 ViewB
关闭。
为防止这种情况,我们需要避免重建 ViewA
。这可以通过使 ViewA
is-a Equatable
来完成,因此 SwiftUI 检查是否需要重新创建 ViewA
,我们将回答否。
事情是这样的:
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