直接设置选择而不是从viewModel设置选择时,SwiftUI NavigationLink行为不同

直接设置选择而不是从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和视图模

我一直在使用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-a
is-a
equalatable
来实现,因此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