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