SwiftUI不会在嵌套NavigationLink目标中使用ObservedObject更新UI

时间:2020-05-11 00:12:30

标签: ios swift swiftui

我目前有一个应用程序正在从API提取数据,在根视图(称为Home)中,一切正常,在第二个视图(称为User View)中,一切正常,但现在在第三个视图(团队视图),仅此视图的ObservedObject不起作用。

最奇怪的是,如果用户直接导航到“团队视图”,那么一切都会按预期进行。

每个视图都有自己的ObservedObject,要加载的数据仅属于该视图

每个视图之间的导航由NavigationLink

进行

这里是我如何进行加载和导航的一个例子。

struct HomeView: View {
   @ObservedObject var viewModel = HomeViewModel()
   var body: some View {
       VStack {
           NavigationLink(destination: UserView(userId: viewModel.userId))
           NavigationLink(destination: TeamView(teamId: viewModel.teamId))
       }
   }
}

struct TeamView: View {
    @ObservedObject var viewModel = TeamViewModel()
    @State var teamId: String = ""
    var body: some View {
        Text(viewModel.name)
            .onAppear() { viewModel.loadData(id: teamId) }
    }
}

struct UserView: View {
    @ObservedObject var viewModel = UserViewModel()
    @State var userId: String = ""
    var body: some View {
        VStack {
            Text(viewModel.name)
            NavigationLink(destination: TeamView(teamId: viewModel.teamId))
        }
            .onAppear() { viewModel.loadData(id: userId) }
    }
}

从示例中您可以看到,用于加载数据的函数在视图模型中,并在视图出现时加载。

一切正常,但是当我到达堆栈的第3级时,数据不会在视图中更新。我以为可能是线程,但是提取完成后我正在使用DispatchQueue.main.async

模型上所有必需的变量都标记为@Published

总而言之,以下流程起作用

HomeView -> TeamView
HomeView -> UserView

但是在最后一个视图上,它确实加载了数据,但没有更新视图

HomeView -> UserView -> TeamView

1 个答案:

答案 0 :(得分:1)

我复制了您的代码行为,该问题是由于快速导航引起的。这是怎么回事

如果愿意的话

HomeView [tap] -> UserView -> [wait for user loaded] -> TeamView // no issue

但是你可以

HomeView [tap] -> UserView [tap] -> TeamView // got defect

缺陷在于,因为UserView在加载数据时在后台更新,所以body被重建,链接被重新创建,TeamView被重建,而.onAppear被重建不会被调用,因为这种视图已经出现在屏幕上了。

(我不确定这是否是SwiftUI错误,因为这种行为有逻辑)。

因此,这是此情况的解决方案。经过Xcode 11.5b的测试。

struct TeamView: View {
    @ObservedObject var viewModel = TeamViewModel()
    var teamId: String  // << state is not needed

    init(teamId: String) {
        self.teamId = teamId
        viewModel.loadData(id: teamId)    // initiate load here !!
    }

    var body: some View {
        Text(viewModel.name)
    }
}

struct UserView: View {
    @ObservedObject var viewModel = UserViewModel()
    @State var userId: String = ""
    var body: some View {
        VStack {
            Text(viewModel.name)

            // used DeferView to avoid in-advance constructions
            NavigationLink(destination: DeferView { TeamView(teamId: viewModel.teamId) })
        }
            .onAppear() { viewModel.loadData(id: userId) }
    }
}

DeferView来自我的帖子this