画面遷移先でグローバルなオブジェクトのメンバを変更する
このページに書いている内容よりも、下記のページに書いてある方法が良いです。
ここから書いている内容はあまり良い方法ではないので、見る価値ないです。いちおう記録的な意味合いで残しておきますが。
アプリケーションの動作パラメータを設定する場合、別の画面にモーダル遷移して値を設定するシチュエーションが多いと思います。これを実現する方法を紹介します。この方法がベストかどうかわかりませんがシンプルでわかりやすいと思います。
下記が管理したいパラメータです。ParametersSystemOperation というのがグローバルに扱いたいクラスで、そこには ParametersMemberAtoF という整数(Int)をメンバに持つクラスと、ParametersMemberUtoZ という小数(Double)をメンバに持つクラスを抱えているとします。
import Foundation
class ParametersMemberAtoF {
var MemberA = 0
var MemberB = 0
var MemberC = 0
var MemberD = 0
var MemberE = 0
var MemberF = 0
init(){
MemberA = 10
MemberB = 20
MemberC = 30
MemberD = 40
MemberE = 50
MemberF = 60
}
}
class ParametersMemberUtoZ {
var MemberU = 0.0
var MemberV = 0.0
var MemberW = 0.0
var MemberX = 0.0
var MemberY = 0.0
var MemberZ = 0.0
init(){
MemberU = 1.0
MemberV = 10.0
MemberW = 100.0
MemberX = 1000.0
MemberY = 10000.0
MemberZ = 100000.0
}
}
class ParametersSystemOperation: ObservableObject {
@Published var PrmsAtoF: ParametersMemberAtoF
@Published var PrmsUtoZ: ParametersMemberUtoZ
init (){
PrmsAtoF = ParametersMemberAtoF()
PrmsUtoZ = ParametersMemberUtoZ()
}
}
次に、プロジェクト名を aaa とした場合は、aaaApp.swift というファイルがデフォルトで存在するはずなので、そこで ContentView クラス以下でグローバルに扱えるクラスとして ParametersSystemOperation のインスタンスを登録します。
import SwiftUI
@main
struct aaaApp: App {
var body: some Scene {
WindowGroup {
// パラメータを持つクラスを関連づける.
ContentView()
.environmentObject( ParametersSystemOperation() )
}
}
}
下記が ContentView です、つまりトップレベルの画面となります。そこでは @EnvironmentObject var Ps: ParametersSystemOperation という変数を定義します。これが aaaApp.swift において関連づけたグローバルなクラスへの参照になります。
@EnvironmentObject で参照した変数は @State な画面更新と関連付けられた変数ではないので、Ps で参照した先のメンバ変数が変更されても、画面の更新はされません。そこで仕方なく @State な変数 memberA と memberU を定義します。
画面が更新されなければいけないタイミングは2箇所です。画面が最初に表示されたとき、モーダル遷移画面から戻ってきたとき、の2箇所です。
58、59行目が、画面が最初に表示されたときに実行されるコードです。
50、51行目が、モーダル遷移画面から戻ってきたときに実行されるコードです。
import SwiftUI
struct ContentView: View {
@EnvironmentObject var Ps: ParametersSystemOperation
@State var FlagShow = false
let TXT_SIZE = 20.0
let TXT_PADDING = 5.0
@State var memberA: Int = 0
@State var memberU: Double = 0.0
var body: some View {
ZStack {
Color( .lightGray )
VStack{
Spacer()
Text( String( format: "A: %d", memberA ) )
.foregroundColor( .white )
Text( String( format: "U: %f", memberU ) )
.foregroundColor( .white )
Spacer()
Button( action:{
FlagShow = true
})
{
Text( "parameters" )
.font( .system( size: TXT_SIZE ) )
.foregroundColor( .white )
}
.padding( TXT_PADDING )
.background( .blue )
Spacer()
}
.sheet( isPresented: $FlagShow, onDismiss: {
// モーダルから Dismiss で戻ってきたときの処理.
memberA = Ps.PrmsAtoF.MemberA
memberU = Ps.PrmsUtoZ.MemberU
}) {
View000()
}
}.onAppear() {
// 画面が表示されたときの処理.
memberA = Ps.PrmsAtoF.MemberA
memberU = Ps.PrmsUtoZ.MemberU
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
// aaaApp.swift と同様に、プレビューコードとしてパラメータを持つクラスを関連づける.
ContentView()
.environmentObject( ParametersSystemOperation() )
}
}
下記がモーダル遷移先で表示される画面 View000 のコードです。
Text を Stepper で囲んで、値をボタンで増減できるようにしました。このビューでも @EnvironmentObject の値は @State な値ではありませんので、仕方なく @State な変数を多数定義します。
こちらはすべての変数を画面更新したいので、大量の @State 変数を宣言しなくてはなりません。
@State な変数をグローバルなクラスへの参照 Ps のメンバに代入するタイミング、また、その逆は2箇所あります。
87〜103行目、このビューが表示されたときに Ps の値を @State な変数に代入します。
58〜70行目、ボタンが押されたときに @State な変数の値を、Ps のメンバに代入します。その後、画面を閉じて元に戻ります(Dismiss)。
ボタンを押さずに画面を下にスワイプさせて閉じた場合は、Ps のメンバは変更されません。
import SwiftUI
struct View000: View {
@EnvironmentObject var Ps: ParametersSystemOperation
@Environment( \.dismiss ) var my_dismiss_action
let BTN_TXT_SIZE = 20.0
let BTN_TXT_PADDING = 10.0
let LIST_TXT_SIZE = 12.0
@State var memberA :Int = 0
@State var memberB :Int = 0
@State var memberC :Int = 0
@State var memberD :Int = 0
@State var memberE :Int = 0
@State var memberG :Int = 0
@State var memberU :Double = 0.0
@State var memberV :Double = 0.0
@State var memberW :Double = 0.0
@State var memberX :Double = 0.0
@State var memberY :Double = 0.0
@State var memberZ :Double = 0.0
var body: some View {
VStack {
Spacer()
List {
Stepper(value: $memberA, in: 0 ... 1024, step: 1 ){ Text( String( format: "A: %d", memberA )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberB, in: 0 ... 1024, step: 1 ){ Text( String( format: "B: %d", memberB )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberC, in: 0 ... 1024, step: 1 ){ Text( String( format: "C: %d", memberC )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberD, in: 0 ... 1024, step: 1 ){ Text( String( format: "D: %d", memberD )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberE, in: 0 ... 1024, step: 1 ){ Text( String( format: "E: %d", memberE )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberG, in: 0 ... 1024, step: 1 ){ Text( String( format: "F: %d", memberG )).font( .system( size: LIST_TXT_SIZE )) }
}
Spacer()
List {
Stepper(value: $memberU, in: 0 ... 999999.9, step: 0.01 ){ Text( String( format: "U: %f", memberU )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberV, in: 0 ... 999999.9, step: 0.01 ){ Text( String( format: "V: %f", memberV )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberW, in: 0 ... 999999.9, step: 0.01 ){ Text( String( format: "W: %f", memberW )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberX, in: 0 ... 999999.9, step: 0.01 ){ Text( String( format: "X: %f", memberX )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberY, in: 0 ... 999999.9, step: 0.01 ){ Text( String( format: "Y: %f", memberY )).font( .system( size: LIST_TXT_SIZE )) }
Stepper(value: $memberZ, in: 0 ... 999999.9, step: 0.01 ){ Text( String( format: "Z: %f", memberZ )).font( .system( size: LIST_TXT_SIZE )) }
}
Spacer()
Button( action:{
Ps.PrmsAtoF.MemberA = memberA
Ps.PrmsAtoF.MemberB = memberB
Ps.PrmsAtoF.MemberC = memberC
Ps.PrmsAtoF.MemberD = memberD
Ps.PrmsAtoF.MemberE = memberE
Ps.PrmsAtoF.MemberF = memberG
Ps.PrmsUtoZ.MemberU = memberU
Ps.PrmsUtoZ.MemberV = memberV
Ps.PrmsUtoZ.MemberW = memberW
Ps.PrmsUtoZ.MemberX = memberX
Ps.PrmsUtoZ.MemberY = memberY
Ps.PrmsUtoZ.MemberZ = memberU
// 画面を閉じて呼び出し画面に戻る.
my_dismiss_action()
})
{
Text( "Change & Close" )
.font( .system( size: BTN_TXT_SIZE ))
.foregroundColor( .white )
}
.padding( BTN_TXT_PADDING )
.background( .blue )
Spacer()
}
.onAppear(){
memberA = Ps.PrmsAtoF.MemberA
memberB = Ps.PrmsAtoF.MemberB
memberC = Ps.PrmsAtoF.MemberC
memberD = Ps.PrmsAtoF.MemberD
memberE = Ps.PrmsAtoF.MemberE
memberG = Ps.PrmsAtoF.MemberF
memberU = Ps.PrmsUtoZ.MemberU
memberV = Ps.PrmsUtoZ.MemberV
memberW = Ps.PrmsUtoZ.MemberW
memberX = Ps.PrmsUtoZ.MemberX
memberY = Ps.PrmsUtoZ.MemberY
memberZ = Ps.PrmsUtoZ.MemberZ
}
}
}
struct View000_Previews: PreviewProvider {
static var previews: some View {
// aaaApp.swift と同様に、プレビューコードとしてパラメータを持つクラスを関連づける.
View000()
.environmentObject( ParametersSystemOperation() )
}
}
グローバルに参照できるクラスを @EnvoronmentObject として扱う限りは、そのメンバは @State な動きができないようです。
なので仕方なくメンバ変数と同じような @State な変数を定義しました。何かいい方法がみつかったら、本サイトでご紹介する予定です。もっともっと調査してみます。
2024/07/13 追加: もっともっと調査してみたら下記のURLのような方法が見つかりました。