SwiftUI不会更新第二个NavigationLink目标 [英] SwiftUI doesn't update second NavigationLink destination

查看:129
本文介绍了SwiftUI不会更新第二个NavigationLink目标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含行的List,这些行可以推动View.该视图具有另一个List,该List推动了另一个View.当数据更改时,原始列表和第一个推送的列表将更新.但是,最后一个视图在推送时不会更新.而且,当我向后滑动时,视图不再更新,即使过去也是如此.

I have a List with rows which push a View. That view has another List which pushing another View. The original List, and the first pushed List will update when the data changes. However, the last view does not update when pushed. And when I swipe back the view no longer updates, even though it used to.

HomeView> UserView> ItemView

HomeView > UserView > ItemView

用户和项目是可识别的结构.我曾尝试将它们设置为Hashable并使用id: \.self,但这似乎也不起作用.

User and Item are structs which are Identifiable. I've tried making them Hashable and using id: \.self, but that didn't seem to work either.

class App: ObservableObject {
    @Published var users = [User]()
}

struct HomeView {

    @EnvironmentObject var app: App

    var body {
        List {
            Section {
                ForEach(app.users) { user in
                    NavigationLink(destination: UserView(user: user)) {
                        Text(user.name)
                    }
                }
            }
        }
    }

}

// Updates fine when `app.users` updates
// Stops updating after going back from ItemView
struct UserView {

    let user: User

    var body {
        List {
            Section {
                ForEach(user.items) { item in
                    NavigationLink(destination: ItemView(user: user, item: item)) {
                        Text(item.name)
                    }
                }
            }
        }
    }

}

/// Does not update when app.users updates
struct ItemView {

    let user: User
    let item: Item

    var body {
        List {
            Section {
                ForEach(item.details) { detail in
                    Text(detail)
                }
            }
        }
    }

}

推荐答案

经过长时间的搜索,我想出了一些我在互联网上其他地方尚未看到的东西.我正在使用@State从父视图初始化我的视图,并使用onReceive更新它.此外,onReceive/onAppear会检查该项是否仍然有效,并在需要时弹出视图.进行初始化以设置国家并将其设为私有可能更正确.

After long searches I've come up with something I have yet to see elsewhere on the internet. I am initializing my views with a @State from the parent view and updating it using onReceive. Further, onReceive/onAppear checks if the item is still valid and pops the view if needed. It might be more correct to make an init to set the State and make that private.

这样做的主要原因是导致崩溃.这是我用来测试更改和删除源代码的完整示例.

The main reason for this was deleting was causing crashes. Here's a complete example I made to test changing and deleting the source.

struct Item: Identifiable {
    var id: String
    var name: String
    var accounts: [Account]
}

struct Account: Identifiable {
    var id: String
    var name: String
}

class App: ObservableObject {
    @Published var items: [Item] = [
        Item(id: "a", name: "A", accounts: [
            Account(id: "1", name: "one"),
            Account(id: "2", name: "two"),
            Account(id: "3", name: "three")
        ])
    ]
}

struct RootView: View {
    var body: some View {
        NavigationView {
            ContentView().environmentObject(App())
        }
    }
}

struct ContentView: View {

    @EnvironmentObject var app: App

    var body: some View {
        List {
            ForEach(app.items) { item in
                NavigationLink(destination: ItemView(item: item)) {
                    Text("\(item.id) - \(item.name)")
                }
            }
            Button(action: { self.app.items[0].name = "XXX" }) {
                Text("Change Item Name")
            }
            Button(action: { self.app.items = [] }) {
                Text("Clear")
            }
        }
    }

}

struct ItemView: View {

    @Environment(\.presentationMode) var presentationMode
    @EnvironmentObject var app: App

    @State var item: Item

    var body: some View {
        List {
            Text("\(item.id) - \(item.name)")
            ForEach(item.accounts) { account in
                NavigationLink(destination: AccountView(item: self.item, account: account)) {
                    Text("\(account.id) - \(account.name)")
                }
            }
            Button(action: { self.app.items[0].name = "XXX" }) {
                Text("Change Item Name")
            }
            Button(action: { self.app.items[0].accounts[0].name = "AAA" }) {
                Text("Change Account Name")
            }
            Button(action: { self.app.items = [] }) {
                Text("Clear")
            }
        }
        .onReceive(app.$items) { items in
            guard let item = items.first(where: { $0.id == self.item.id }) else {
                self.presentationMode.wrappedValue.dismiss()
                return
            }
            self.item = item
        }
        .onAppear {
            if !self.app.items.contains(where: { $0.id == self.item.id }) {
                self.presentationMode.wrappedValue.dismiss()
            }
        }
    }

}

struct AccountView: View {

    @Environment(\.presentationMode) var presentationMode
    @EnvironmentObject var app: App

    @State var item: Item
    @State var account: Account

    var body: some View {
        List {
            Text("\(item.id) - \(item.name)")
            Text("\(account.id) - \(account.name)")
            Button(action: { self.app.items[0].name = "XXX" }) {
                Text("Change Item Name")
            }
            Button(action: { self.app.items[0].accounts[0].name = "AAA" }) {
                Text("Change Account Name")
            }
            Button(action: { self.app.items = [] }) {
                Text("Clear")
            }
        }
        .onReceive(app.$items) { items in
            guard
                let item = items.first(where: { $0.id == self.item.id }),
                let account = item.accounts.first(where: { $0.id == self.account.id })
            else {
                self.presentationMode.wrappedValue.dismiss()
                return
            }
            self.item = item
            self.account = account
        }
    }
}

这篇关于SwiftUI不会更新第二个NavigationLink目标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆