直接设置选择而不是从viewModel设置选择时,SwiftUI NavigationLink行为不同
我一直在使用SwiftUI,遇到了意想不到的行为 我有视图A、视图B和视图C。视图C有更改视图A中AppState的Environment对象 视图B具有带选项的ViewModel 如果我从ViewModel调用函数来更改选择,那么 视图C显示几秒钟,然后自动弹出到视图B 如果我直接从视图B(而不是从ViewModel)更改选择,则一切都会按预期进行。 此外,如果我对onDissapear进行注释,它也会起作用。但是,当屏幕消失时,我需要更改environmentObject 这是视图B和视图模型直接设置选择而不是从viewModel设置选择时,SwiftUI NavigationLink行为不同,swiftui,swiftui-navigationlink,swiftui-navigationview,Swiftui,Swiftui Navigationlink,Swiftui Navigationview,我一直在使用SwiftUI,遇到了意想不到的行为 我有视图A、视图B和视图C。视图C有更改视图A中AppState的Environment对象 视图B具有带选项的ViewModel 如果我从ViewModel调用函数来更改选择,那么 视图C显示几秒钟,然后自动弹出到视图B 如果我直接从视图B(而不是从ViewModel)更改选择,则一切都会按预期进行。 此外,如果我对onDissapear进行注释,它也会起作用。但是,当屏幕消失时,我需要更改environmentObject 这是视图B和视图模
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
}
}
}
我明白了。。。这和《邮报》有点不同。这个问题是因为视图模型被重新创建(这是长期观察到的
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-ais-aequalatable
来实现,因此SwiftUI检查是否需要重新创建ViewA
,我们将回答否
事情是这样的:
NavigationLink(
destination: ViewA().equatable(), // << here !!
tag: 1,
selection: $selection,
label: { EmptyView()})
与Xcode 12.1/iOS 14.1的工作原理相同。您使用哪种环境?@Asperi我的环境是相同的。有一刻,我创建了一个最低git回购协议,该问题是可复制的我更新了当前代码,所以只需复制它并从场景委派中设置env object就足够了。您能否更具体地说明如何将ViewModel的所有权保留在ViewA之外?我尝试使用factory模式创建静态func,以使用已注入的ViewModel创建ViewA,但它没有解决问题。我用代码更新了我的问题我仍然想支持ios 13:(
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