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")
}
})
}
}
うまくいきました!