タブレット用プログラムの書き止め

android OS & iPadOS の記録。

【Swift P4.5.1】広域変数について

2024-10-25 16:16:06 | Swift iPadOS用

@StateObject
ObservableObject
@EnvironmentObject

これらを利用することでほぼ全てでアクセス可能な変数が作れる。
ただ、変数が更新されると関連付けられているViewも自動更新されるのでルールが有り制限も受ける。
勘違いによるデータの書き換え等の抑制のためにルールが存在する。

制限を受けたくない場合はグローバル変数を使う。
個人が全てを管理してチェックを厳しく行わないとバグが発生する。


共有変数を body{ } 内で利用しないと、エラーもしくはクラッシュを引き起こす。
共有変数は関連項目に自動更新を施すためにその枠外の変数に写し利用されると問題が起こりやすくなる。


body { } 内で共有変数の値を利用して、手順をもって共有変数に代入する事。
bodyプロパティに @ViewBuilderアトリビュートが付いているそうで 宣言式以外ではViewが戻らないとエラーとなる。

let image = 共有変数 // OK
共有変数 = 値    // NG

Button { // コード部分
 共有変数 = 値 // OK
} label: { }

UI のコード部分での代入はUIがViewを戻すので問題ないみたい。

@StateObjectにするか、グローバル変数にするか練習が終わってからかな。

 

【ChildView.swift】

struct ChildView: View {
    @EnvironmentObject var dataModel : DataModel

    /******** 【NG】 エラーは出ないがクラッシュする

    @State private var image: UIImage?
    @State private var imageSize: CGSize = CGSize.zero
    init() {
        image = dataModel.secoundImage
        imageSize =  image!.size
    }
    var body: some View {
        Image(uiImage: image!)
            .resizable()
            .scaledToFit()
            .frame(width: imageSize.width )
    }

    **********/


    @State private var isChange: Bool = true
    var body: some View {
        let image = dataModel.secoundImage
        if  image != nil {
            Image(uiImage: image!)
                .resizable()
                .scaledToFit()
                .frame( width: image!.size.width )

        } else {
            Text("no data")
            // 共有変数に代入の式ではViewを返さないのでエラー。UIの実行部分に記述すればOK。
            // dataModel.secoundImage = UIImage(named: "Test2") 代入には場所を選ぶ。if文内はダメ。
        }
        Button { // ButtonはViewを返しているのでエラーにならない
            dataModel.secoundImage = UIImage(named: isChange ? "Test2": "Test")
            isChange.toggle()
        } label: {
            Text("Change")
        }
    }
}

【ContentView.swift】

struct ContentView: View {
   @EnvironmentObject var dataModel: DataModel

    var body: some View {
        VStack {
            if let image = dataModel.originalImage
            {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
            }
        }
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .navigationBarLeading) {
                Text("Child ").bold()
            }
            ToolbarItem(placement: .navigationBarLeading) {
                NavigationLink {
                    ChildView()
                        .environmentObject(dataModel) // 必須
                } label: {
                    Image(systemName: "crop")
                }
            }
        }
    }
}

【Main.swift】

@main
struct MyApp: App {
    @StateObject var dataModel = DataModel()
    var body: some Scene {
        WindowGroup {
            NavigationStack {
                ContentView()
                    .environmentObject(dataModel) // 必須
            }
            .navigationViewStyle(.stack)
        }
    }
}

【DataModel.swift】

class DataModel: ObservableObject {
    @Published var originalImage: UIImage? = nil
    @Published var secoundImage: UIImage? = nil

    init() {
        originalImage = UIImage(named: "Test3")
        secoundImage = UIImage(named: "Test")
    }
}

 

この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« 【Swift P4.5.1】Retinaディ... | トップ | 【Swift P4.5.1】SideBar »

Swift iPadOS用 」カテゴリの最新記事