ForEachには、、、を渡して、そしてitemという名前で扱えるようになっている。
itemにはフラグがあり、ボタンをタップした時に切り替えをするので代入する。
しかしViewに引数として渡した値はデフォルトで定数になります。
なので、以下のようにvar item = koumoku
と宣言して変数にしています。
import SwiftUI /** 美容師の施術メニュー(大カテ:カットやシャンプーなど) */ class HairDresserMenu:Identifiable, ObservableObject{ public var id = UUID() public var name:String @Published public var isChecked:Bool public var time:String public var cost:String public var menu_list:Array< HairDresserMenu >? var isDirectory: Bool { return menu_list != nil } init(_ name:String, _ isChecked:Bool, _ time:String, _ cost:String, _ menu_list:Array< HairDresserMenu >?){ self.name = name self.isChecked = isChecked self.time = time self.cost = cost self.menu_list = menu_list } } /** ViewModelの構想 */ class ViewModel1:ObservableObject{ @Published public var menuList:Array< HairDresserMenu > init(){ self.menuList = [HairDresserMenu("カット", false, "","", [HairDresserMenu("カット1", false, "10", "1000", nil), HairDresserMenu("カット2", false, "20", "2000", nil), HairDresserMenu("カット3", false, "30", "3000", nil)] ), HairDresserMenu("トリートメント",false,"","", [HairDresserMenu("トリートメント1",false,"40","4000",nil), HairDresserMenu("トリートメント2",false,"50","5000", nil), HairDresserMenu("トリートメント3",false,"60","6000", nil)] )] } } /** ViewModelの構想 */ class ViewModel2:ObservableObject{ @Published public var MenuList:ViewModel1 = ViewModel1() } struct ServiceMenuDetail2:View{ @ObservedObject public var viewmodel:ViewModel2 = ViewModel2() // このメニューをFirestoreから取ってくるときは、このViewが表示されたときのみにする?であれば.onAppear()で埋め込み処理だよねー。 // 純粋にメニューってそんな多くないからForEachで回して足し算する? // チェック入ったらflagでtrueにして、trueのもののみ足す init(){ UITableView.appearance().backgroundColor = UIColor(red: 248/255, green: 248/255, blue: 255/255, alpha: 1) } var body: some View{ VStack(){ List(viewmodel.MenuList.menuList, children:\.menu_list) { koumoku in // このidはないか確認する(そしてchildren:はもう定義でchildrenを使わないとダメらしい) HStack { if(koumoku.isDirectory){ Text(koumoku.name) .font(.custom("BodoniSvtyTwoITCTT-Bold", size: 16)) .font(.system(size: 20, weight: .bold)) } else { var item = koumoku // koumokuは定数になるので、一回変数に変換。そうしないと変換が走らない。そしてあくまでこのServiceMenuDetail2では監視対象がviewmodelであって、itemではないので、別Viewでitemが監視対象になるようにしないといけない。 Text(item.name+" \(item.time)分 \(item.cost)円") .font(.custom("BodoniSvtyTwoITCTT-Bold", size: 16)) .foregroundColor(Color(red:105/255, green:105/255, blue:105/255)) Spacer() Button(action: { item.isChecked = !item.isChecked }, label: { if(item.isChecked) { Image(systemName: "checkmark.square.fill") .foregroundColor(.green) } else { Image(systemName: "square") } }) } } } } } } /** 関数の引数で渡した値はデフォルトで定数になる。 そのため、もし値の更新などを行いたい場合は、一回変数として再宣言させる必要がある。 https://samekard.blogspot.com/2014/09/swifterror.html Command CompileSwiftSources failed with a nonzero exit codeってエラーになった。 */
これで実行してみると、listをタップしてもViewの再構築が走りませんでした。
このServiceMenuDetail2というViewでは監視対象が、@ObservedObject宣言した、viewmodelである。このviewmodel自体に更新が走った場合に再構築が走る。
タップした際の処理では、item. = item.
で更新しているが、このitemは更新対象ではない。このitemに対して@Stateや@ObservedObjectなどで宣言して、このServiceMenuDetail2内で監視対象になっているわけではないからです。
なので、これを監視対象にしたいのであれば、ForEach内で宣言はできないので、
一度別のViewに切り出して、引数で渡し、
そのViewの受け取り先でitemに対して@ObservedObject宣言をします。(itemはMenuListクラスであり、このクラスではObservableObjectに準拠しているので)
import SwiftUI /** 美容師の施術メニュー(大カテ:カットやシャンプーなど) */ class HairDresserMenu:Identifiable, ObservableObject{ public var id = UUID() public var name:String @Published public var isChecked:Bool public var time:String public var cost:String public var menu_list:Array? var isDirectory: Bool { return menu_list != nil } init(_ name:String, _ isChecked:Bool, _ time:String, _ cost:String, _ menu_list:Array ?){ self.name = name self.isChecked = isChecked self.time = time self.cost = cost self.menu_list = menu_list } } /** ViewModelの構想 */ class ViewModel1:ObservableObject{ @Published public var menuList:Array init(){ self.menuList = [HairDresserMenu("カット", false, "","", [HairDresserMenu("カット1", false, "10", "1000", nil), HairDresserMenu("カット2", false, "20", "2000", nil), HairDresserMenu("カット3", false, "30", "3000", nil)] ), HairDresserMenu("トリートメント",false,"","", [HairDresserMenu("トリートメント1",false,"40","4000",nil), HairDresserMenu("トリートメント2",false,"50","5000", nil), HairDresserMenu("トリートメント3",false,"60","6000", nil)] )] } } /** ViewModelの構想 */ class ViewModel2:ObservableObject{ @Published public var MenuList:ViewModel1 = ViewModel1() } struct ServiceMenuDetail2:View{ @ObservedObject public var viewmodel:ViewModel2 = ViewModel2() // このメニューをFirestoreから取ってくるときは、このViewが表示されたときのみにする?であれば.onAppear()で埋め込み処理だよねー。 // 純粋にメニューってそんな多くないからForEachで回して足し算する? // チェック入ったらflagでtrueにして、trueのもののみ足す init(){ UITableView.appearance().backgroundColor = UIColor(red: 248/255, green: 248/255, blue: 255/255, alpha: 1) } var body: some View{ VStack(){ List(viewmodel.MenuList.menuList, children:\.menu_list) { koumoku in // このidはないか確認する(そしてchildren:はもう定義でchildrenを使わないとダメらしい) HStack { if(koumoku.isDirectory){ Text(koumoku.name) .font(.custom("BodoniSvtyTwoITCTT-Bold", size: 16)) .font(.system(size: 20, weight: .bold)) } else { var item = koumoku // koumokuは定数になるので、一回変数に変換。そうしないと変換が走らない。そしてあくまでこのServiceMenuDetail2では監視対象がviewmodelであって、itemではないので、別Viewでitemが監視対象になるようにしないといけない。 aaaaaaaaaa(item: item) // Text(item.name+" \(item.time)分 \(item.cost)円") // .font(.custom("BodoniSvtyTwoITCTT-Bold", size: 16)) // .foregroundColor(Color(red:105/255, green:105/255, blue:105/255)) // Spacer() // Button(action: { // item.isChecked = !item.isChecked // }, label: { // if(item.isChecked) { // Image(systemName: "checkmark.square.fill") // .foregroundColor(.green) // } else { // Image(systemName: "square") // } // }) } } } } } } struct aaaaaaaaaa:View{ @ObservedObject public var item:HairDresserMenu var body: some View{ Text(item.name+" \(item.time)分 \(item.cost)円") .font(.custom("BodoniSvtyTwoITCTT-Bold", size: 16)) .foregroundColor(Color(red:105/255, green:105/255, blue:105/255)) Spacer() Button(action: { item.isChecked = !item.isChecked }, label: { if(item.isChecked) { Image(systemName: "checkmark.square.fill") .foregroundColor(.green) } else { Image(systemName: "square") } }) } }
うまくいきました!