1. ホーム
  2. swiftui

[解決済み] SwiftUIアプリケーションでObservedObjectの配列が更新されないのはなぜか?

2023-08-02 11:07:49

質問

私はSwiftUIで遊んでいて、どのように ObservableObject がどのように機能するかを理解しようとしています。私は、配列の Person オブジェクトの配列があります。新しい Person を配列に追加すると、それがビューに再ロードされますが、 既存の Person の値を変更しても、ビューに再ロードされません。

//  NamesClass.swift
import Foundation
import SwiftUI
import Combine

class Person: ObservableObject,Identifiable{
    var id: Int
    @Published var name: String
    
    init(id: Int, name: String){
        self.id = id
        self.name = name
    }
}

class People: ObservableObject{
    @Published var people: [Person]
    
    init(){
        self.people = [
            Person(id: 1, name:"Javier"),
            Person(id: 2, name:"Juan"),
            Person(id: 3, name:"Pedro"),
            Person(id: 4, name:"Luis")]
    }
}

struct ContentView: View {
    @ObservedObject var mypeople: People
    
    var body: some View {
        VStack{
            ForEach(mypeople.people){ person in
                Text("\(person.name)")
            }
            Button(action: {
                self.mypeople.people[0].name="Jaime"
                //self.mypeople.people.append(Person(id: 5, name: "John"))
            }) {
                Text("Add/Change name")
            }
        }
    }
}

を追加する行をアンコメントすると、新しい Person (John)を追加すると、Jaimeの名前が正しく表示されますが、名前だけを変更すると、これはViewに表示されません。

私は何か間違ったことをしているのか、あるいは、どのように ObservedObjects がどのように配列で動作するのかを理解していないのかもしれません。

どのように解決するのですか?

クラスの代わりに構造体を使用することができます。構造体の値のセマンティクスにより、人の名前の変更はPerson構造体自体の変更と見なされ、この変更はpeople配列の変更でもあるため、@Publishedで通知が送信され、Viewの本文が再計算されることになります。

import Foundation
import SwiftUI
import Combine

struct Person: Identifiable{
    var id: Int
    var name: String

    init(id: Int, name: String){
        self.id = id
        self.name = name
    }

}

class Model: ObservableObject{
    @Published var people: [Person]

    init(){
        self.people = [
            Person(id: 1, name:"Javier"),
            Person(id: 2, name:"Juan"),
            Person(id: 3, name:"Pedro"),
            Person(id: 4, name:"Luis")]
    }

}

struct ContentView: View {
    @StateObject var model = Model()

    var body: some View {
        VStack{
            ForEach(model.people){ person in
                Text("\(person.name)")
            }
            Button(action: {
                self.mypeople.people[0].name="Jaime"
            }) {
                Text("Add/Change name")
            }
        }
    }
}

あるいは(お勧めしませんが)。 Person はクラスなので、参照型になります。それが変化すると People 配列は変更されないので、サブジェクトからは何も出力されません。しかし、手動で呼び出すことで、それを知らせることができます。

Button(action: {
    self.mypeople.objectWillChange.send()
    self.mypeople.people[0].name="Jaime"    
}) {
    Text("Add/Change name")
}