未分類

Viewに渡した値はデフォルトで定数。さらに監視対象の確認

2022年4月25日

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")
            }
        })
    }
    
}

うまくいきました!

-未分類

© 2022 Yosshi Blog Powered by AFFINGER5