SwiftUI-多层级数据@Published不更新问题

302 阅读1分钟
import SwiftUI

extension MultilayerDataView {
    class People: ObservableObject, Identifiable {
        @Published var peopleList: [Person] = []
        
        init() {
            peopleList = [
                Person(name: "Alice", hobbies: [Hobbie("Reading"), Hobbie("Painting")]),
                Person(name: "Bob", hobbies: [Hobbie("Swimming"), Hobbie("Cooking")])
            ]
        }
    }
    
    class Person: ObservableObject, Identifiable {
        @Published var name: String
        @Published var hobbies: [Hobbie]  // 数组属性
        
        init(name: String, hobbies: [Hobbie]) {
            self.name = name
            self.hobbies = hobbies
        }
    }
    
    class Hobbie: ObservableObject, Identifiable {
        @Published var name: String
        init(_ name: String) {
            self.name = name
        }
    }
}

struct MultilayerDataView: View {
    @StateObject var people: People = People()
    
    var body: some View {
        VStack {
            ForEach(people.peopleList) { person in
                Text(person.name)
                
                ForEach(person.hobbies) { hobby in
                    Text(hobby.name)
                }
                
                Button("Add Person") {
                    people.peopleList.append(Person(name: "Hony", hobbies: [Hobbie("running"), Hobbie("eatting")]))
                }

                Button("Add Hobby") {
                    people.objectWillChange.send() // 重点
                    person.hobbies.append(MultilayerDataView.Hobbie("runing"))
                }
            }
        }
    }
}

struct MultilayerDataView_Previews: PreviewProvider {
    static var previews: some View {
        MultilayerDataView()
    }
}

现象

上面代码, 在没有添加 people.objectWillChange.send() 这行代码前,

添加Person能够自动刷新视图,但是添加Hobby无法自动刷新

原因

多层ForEach导致的

因为hobbies是包含在people.peopleListForEach中,那么更新hobbies中的数据时候,虽然数据更新了,但是由于people.peopleList的数据并没有变化,所以外层ForEach代码根本不会重新执行。随意页面才不会更新。

这也是ForEach的机制,通过id标识,保证每条数据不会重复加载

解决办法

修改数据前,最外层对象调用一下这个函数objectWillChange.send(),标记需要刷新