arrays - 当已发布数组中的对象更新而不是数组本身时,SwiftUI 如何更新 View

标签 arrays swiftui

以下是我面临的问题的一个人为示例。

情况: 我有一个 PersonStore 类,它将 Person 类存储在数组中并已发布。 Person 类为每个 Person 创建一个 LegalAge 模型,并且该模型已发布
问题: 当对 LegalAge 中的人员年龄进行更新时,PersonStore 的 View 不会更新。

我猜测没有变化的原因是因为变化发生在数组内的对象而不是数组本身。我该如何克服这个问题?

Example


import SwiftUI

enum LegalAge: Int, CaseIterable {
    case UK = 18
    case USA = 21
    
    var name: String { "\(self)" }
}

struct ContentView: View {
    var body: some View {
        MainView()
        
    }
}

/// Displays a list of people and their ages
struct MainView: View {
    @ObservedObject var personStore = PersonStore()
    
    @State private var addPerson = false
    
    var body: some View {
        NavigationView {
            List {
                ForEach(personStore.store) { person in
                    NavigationLink(destination: ShowAge(person: person)) {
                        HStack {
                            Text("\(person.name)")
                            Text("\(person.model.personsAge)")
                            Text("\(person.model.country)")
                            Image(systemName: (person.model.personsAge >= person.model.drinkingAge) ? "checkmark.shield.fill" : "xmark.shield.fill").foregroundColor((person.model.personsAge >= person.model.drinkingAge) ? Color.green : Color.red)
                        }
                    }
                }
            }
            .navigationBarTitle(Text("Can I Drink?"))
            .navigationBarItems(leading: Button(action: { addPerson = true }) { Image(systemName: "plus") }
            )
        }
        /// Open a sheet to create a new person.
        .sheet(isPresented: $addPerson) { AddPerson().environmentObject(personStore) }
    }
}

/// A Person store to hold all the people.
class PersonStore: ObservableObject {
    
    @Published var store = [Person]()
    
    
    func addPerson(person: Person) {
        store.append(person)
    }
    
    func getPerson(name: String) -> Person? {
        if let nameAtIndex = store.firstIndex(where: {$0.name == name}) {
            return store[nameAtIndex]
        }
        
        return nil
    }
    
    func insertPerson(person: Person) {
        store.append(person)
    }
}

/// Form to allow adding people.
struct AddPerson: View {
    @EnvironmentObject var personStore: PersonStore
    
    @State private var name: String = ""
    @State private var age: String = ""
    @State private var country: LegalAge = LegalAge.UK
    
    var body: some View {
        NavigationView {
            Form {
                TextField("Name", text: $name)
                TextField("Age", text: $age)
                Picker(selection: $country, label: Text("Country")) {
                        ForEach(LegalAge.allCases, id: \.self) { c in
                            Text("\(c.name)").tag(c)
                        }
                }
                Section {
                    Button(action: { personStore.addPerson(person: Person(name: name, age: Int(age) ?? 18, country: country))}, label: { Text("Add person" )})
                }
            }
        }
    }
}

/// View to show peoples details and allow some of these details to be edited.
struct ShowAge: View {
    @ObservedObject var person: Person
    @State private var showAgeEditor = false
    
    var body: some View {
        VStack {
            /// Show the current age.
            Text("\(self.person.name)")
            Text("\(self.person.model.personsAge)")
            Text("\(self.person.model.country)")
            Image(systemName: "keyboard")
            .onTapGesture {
                self.showAgeEditor = true
            }
            /// Present the sheet to update the age.
            .sheet(isPresented: $showAgeEditor) {
                SheetView(showAgeEditor: self.$showAgeEditor)
                .environmentObject(self.person)
                .frame(minWidth: 300, minHeight: 400)
            }
        }
    }
}

/// Sheet to allow editing of persons details.
struct SheetView: View {
    @EnvironmentObject var person: Person
    @Binding var showAgeEditor: Bool

    let maxAge = 21
    let minAge = 16
    
    var body: some View {
        return VStack(alignment: .leading) {
            Text("Name: \(person.name)")
            Stepper(value: $person.model.personsAge, in: minAge...maxAge, step: 1) {
                Text("Age: \(person.model.personsAge)")
            }
            Text("Country: \(person.model.country)")
        }
    }
}

/// ViewModel that creates the Person Object to stored.
class Person: ObservableObject, Identifiable {
    @Published var model: LegalDrinkingAge
    var name: String
    var id = UUID()
    
    init(name: String, age:Int, country: LegalAge) {
        self.name = name
        self.model = Person.createLegalAge(drinkingAge: country.rawValue, country: "\(country)", personsAge: age)
    }
    
    private static func createLegalAge(drinkingAge: Int, country: String, personsAge: Int) -> LegalDrinkingAge {
        LegalDrinkingAge(drinkingAge: drinkingAge, country: country, personsAge: personsAge)
    }
    
    func updateAge(_ age: Int) {
        model.personsAge = age
    }
}

struct LegalDrinkingAge {
    var drinkingAge: Int = 0
    var country: String = ""
    var personsAge: Int = 0
    
    mutating func setDrinkingAge(age: Int, country: String, personsAge: Int) {
        drinkingAge = age
        self.country = country
        self.personsAge = personsAge
    }
}


最佳答案

分离 View 并使其可供人观察

class StorePersonRowView: View {
   @ObservedObject person: Person

   var body: some View {
     HStack {
        Text("\(person.name)")
        Text("\(person.model.personsAge)")
        Text("\(person.model.country)")
        Image(systemName: (person.model.personsAge >= person.model.drinkingAge) ? "checkmark.shield.fill" : "xmark.shield.fill").foregroundColor((person.model.personsAge >= person.model.drinkingAge) ? Color.green : Color.red)
     }
   }
}

并在链接中使用它

ForEach(personStore.store) { person in
    NavigationLink(destination: ShowAge(person: person)) {
       StorePersonRowView(person: person)
    }
}

关于arrays - 当已发布数组中的对象更新而不是数组本身时,SwiftUI 如何更新 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63156311/

相关文章:

javascript - 数组仍为空

arrays - 如何在 RethinkDB 中返回过滤后的嵌套数组?

php - 在 php 中如何设置数组的大小?

php - 使用简单的 html dom 获取 url-data 属性

ios - 如何让 SKEmitterNode 在 SwiftUI 中工作?

ios - 如何删除 SwiftUI NavigationView 中的默认导航栏空间

c++ - 静态二维数组

ios - 错误消息: Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeead03e18)

swift - 如何停止动画出现在 SwiftUI 中的 View ?

ios - SwiftUI:如何在 macOS 中使用 NavigationView?