SwiftUI doesn't update second NavigationLink destination

后端 未结 3 1420
小蘑菇
小蘑菇 2021-01-14 00:48

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 chan

相关标签:
3条回答
  • 2021-01-14 01:29

    Ok, I think I'm starting to understand what you want. How about something like this:

    struct ContentView: View {
    
    @EnvironmentObject var app: App
    
    var body: some View {
        NavigationView {
            List(0..<app.users.count) { i in
                NavigationLink(destination: UserView(user: self.$app.users[i])) {
                    Text(self.app.users[i].name)
                }
            }
        }
    }
    
    
    struct UserView: View {
    @Binding var user: User
    var body: some View {
        List(0..<user.items.count) { i in
                NavigationLink(destination:
                ItemView(item: self.$user.items[i])) {
                    Text(self.user.items[i].name)
                }
            }
    
    }
    }
    
    struct ItemView: View {
    @Binding var item: Item
    @State var hasChanged = false
    var body: some View {
        VStack {
            Button(action: {
                self.item.details.append(contentsOf: ["wx","wy"])
                self.hasChanged.toggle()
            }) {
                Text("Add an item")
            }
            List(0..<item.details.count, id: \.self) { i in
                Text(self.item.details[i])
            }
        }
    }
    }
    
    0 讨论(0)
  • 2021-01-14 01:30

    This is the test I made and is working well, everything updates as expected.

    struct User: Identifiable {
        var id: String
        var name: String
        var items: [Item]
    }
    
    struct Item: Identifiable {
        var id: String
        var name: String
        var details: [String]
    }
    
    class App: ObservableObject {
        @Published var users = [User]()
    
        init() {
        let items1 = [Item(id: UUID().uuidString, name: "item1", details: ["d1","d2"]), Item(id: UUID().uuidString, name: "item2", details: ["d3","d4"])]
        let items2 = [Item(id: UUID().uuidString, name: "item3", details: ["e1","e2"]), Item(id: UUID().uuidString, name: "item4", details: ["e3","e4"])]
        users.append(User(id: UUID().uuidString, name: "user1", items: items1))
        users.append(User(id: UUID().uuidString, name: "user2", items: items2))
        }
    }
    
    
    struct ContentView: View {
    
    @ObservedObject var app = App()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(app.users) { user in
                    NavigationLink(destination: UserView(user: user)) {
                        Text(user.name)
                    }
                }
            }
        }
    }
    }
    
    
    struct UserView: View {
    @State var user: User
    
    var body: some View {
        List {
            ForEach(user.items) { item in
                NavigationLink(destination: ItemView(item: item)) {
                    Text(item.name)
                }
            }
        }
    }
    }
    
    struct ItemView: View {
    @State var item: Item
    
    var body: some View {
        List {
            ForEach(item.details, id: \.self) { detail in
                Text(detail)
            }
        }
    }
    }
    
    0 讨论(0)
  • 2021-01-14 01:30

    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
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题