複数画面で共有の変数をもつ

要はグローバル変数のような扱いです。public なクラスで public なメンバを持つという極めて危険なやり方ですが、チーム開発でなくて個人開発ならば素早くやりたいことができるのでお勧めの方法です。注意深いベテランならば、この手法で問題がでることは無いと思います。

アプリ起動時は Fig. 1 です。ViewA というところをタップすると数字が増えます。増えたところを Fig. 2 に示します。

Fig. 1
Fig. 2

数字が増えたことを確認したら、画面をスワイプしてみます。すると Fig. 3 や Fig. 4 や Fig. 5 で、同じ値が表示されています。ちがうビューでタップしても、その値はすべてのビューで引き継がれていることが確認できると思います。すなわちグローバル変数として機能していることになります。

Fig. 3
FIg. 4
Fig. 5

まずは public なメンバを持つ public なクラスの定義を ParametersSystemOperation とします。ObservableObject で定義していることと、メンバが @Published で定義されていることに注意してください。ここの指定がない場合、タップしても画面に数値の変化が現れないのでアセることになります。

Observable で Publish なことで、値に変化があったら iOS システムに値の変更があったことを伝え、iOS は画面を再描画するということになります。システムが常に値の変化を監視するよりも、変数が変化したときだけシステムに再描画の要請をしてシステム動作の負荷を下げるという仕組みです。

import Foundation

class ParametersSystemOperation : ObservableObject{

    @Published var Value0: Int
    @Published var Value1: Double

    init(){
        Value0 = 100
        Value1 = 200.0
    }

}

上記のコードで定義した ParametersSystemOperation クラスを、インスタンス Ps としてアプリ起動時に生成します。このファイルはアプリケーションのプロジェクトが aaa の場合 aaaApp.swift という名前になります。

import SwiftUI

@main
struct aaaApp: App {

    var Ps = ParametersSystemOperation()

    var body: some Scene {

        WindowGroup {
            ViewMain( parameters_system_operation: Ps )
        }

    }
}

この Ps をタブビューを管理する ViewMain で受け取ります。コンストラクタ init の中で Ps インスタンスへの参照をコピーします。参照がコピーされる変数も @ObservedObject な var であることに注意してください。

import SwiftUI

struct ViewMain: View {

    @ObservedObject var Ps: ParametersSystemOperation

    init( parameters_system_operation: ParametersSystemOperation ){
        Ps = parameters_system_operation
    }

    var body: some View {

        TabView() {
            ViewA( parameters_system_operation: Ps )
            ViewB( parameters_system_operation: Ps )
            ViewC( parameters_system_operation: Ps )
            ViewD( parameters_system_operation: Ps )
        }
        .tabViewStyle( PageTabViewStyle() )

    }
}

#Preview{
    let dum = ParametersSystemOperation()
    return ViewMain( parameters_system_operation: dum )
}

ViewMain は、タブビューとして ViewA と ViewB と ViewC と ViewD を管理しています。ここに、受け取った Ps への参照をそのまま渡します。

下記が ViewA のコードです。init() の中で、ViewMain と同じく参照をコピーしています。ここでも同じく @ObservedObject な var で受け取ります。

import SwiftUI

struct ViewA: View {

    @ObservedObject var Ps: ParametersSystemOperation

    init( parameters_system_operation: ParametersSystemOperation ){
        Ps = parameters_system_operation
    }

    var body: some View {

        ZStack {

            Color.mint
                .edgesIgnoringSafeArea(.all)

            VStack{

                let str0 = String( format: "Value0 is %d", Ps.Value0 )
                Text( str0 )
                    .font(.title)
                    .foregroundColor(.black)

                let str1 = String( format: "Value1 is %.2f", Ps.Value1 )
                Text( str1 )
                    .font(.title)
                    .foregroundColor(.black)

                Text("ViewA[TAP please]")
                    .font(.largeTitle)
                    .foregroundColor(.black)
                     .onTapGesture {
                        Ps.Value0 += 1
                        Ps.Value1 += 0.1
                    }

            }

        }

    }
}

#Preview{
    let dum = ParametersSystemOperation()
    return ViewA( parameters_system_operation: dum )
}

続いて ViewB のコードです。ViewA と同じ仕組みです。

import SwiftUI

struct ViewB: View {

    @ObservedObject var Ps: ParametersSystemOperation

    init( parameters_system_operation: ParametersSystemOperation ){
        Ps = parameters_system_operation
    }

    var body: some View {
        
        ZStack {
            
            Color.orange
                .edgesIgnoringSafeArea(.all)
            
            VStack{

                let str0 = String( format: "Value0 is %d", Ps.Value0 )
                Text( str0 )
                    .font(.title)
                    .foregroundColor(.black)

                let str1 = String( format: "Value1 is %.2f", Ps.Value1 )
                Text( str1 )
                    .font(.title)
                    .foregroundColor(.black)

                Text("ViewB[TAP please]")
                    .font(.largeTitle)
                    .foregroundColor(.black)
                     .onTapGesture {
                        Ps.Value0 += 1
                        Ps.Value1 += 0.1
                    }

            }
            
        }

    }
}

#Preview{
    let dum = ParametersSystemOperation()
    return ViewB( parameters_system_operation: dum )
}

また続いて ViewC のコードです。ViewA と同じ仕組みです。

import SwiftUI

struct ViewC: View {

    @ObservedObject var Ps: ParametersSystemOperation

    init( parameters_system_operation: ParametersSystemOperation ){
        Ps = parameters_system_operation
    }

    var body: some View {

        ZStack {

            Color.green
                .edgesIgnoringSafeArea(.all)

            VStack{

                let str0 = String( format: "Value0 is %d", Ps.Value0 )
                Text( str0 )
                    .font(.title)
                    .foregroundColor(.black)

                let str1 = String( format: "Value1 is %.2f", Ps.Value1 )
                Text( str1 )
                    .font(.title)
                    .foregroundColor(.black)

                Text("ViewC[TAP please]")
                    .font(.largeTitle)
                    .foregroundColor(.black)
                     .onTapGesture {
                        Ps.Value0 += 1
                        Ps.Value1 += 0.1
                    }

            }

        }

    }
}

#Preview{
    let dum = ParametersSystemOperation()
    return ViewC( parameters_system_operation: dum )
}

しつこいですが続いて ViewD のコードです。ViewA と同じ仕組みです。

import SwiftUI

struct ViewD: View {

    @ObservedObject var Ps: ParametersSystemOperation

    init( parameters_system_operation: ParametersSystemOperation ){
        Ps = parameters_system_operation
    }

    var body: some View {

        ZStack {

            Color.blue
                .edgesIgnoringSafeArea(.all)

            VStack{

                let str0 = String( format: "Value0 is %d", Ps.Value0 )
                Text( str0 )
                    .font(.title)
                    .foregroundColor(.black)

                let str1 = String( format: "Value1 is %.2f", Ps.Value1 )
                Text( str1 )
                    .font(.title)
                    .foregroundColor(.black)

                Text("ViewD[TAP please]")
                    .font(.largeTitle)
                    .foregroundColor(.black)
                     .onTapGesture {
                        Ps.Value0 += 1
                        Ps.Value1 += 0.1
                    }

            }

        }

    }
}

#Preview{
    let dum = ParametersSystemOperation()
    return ViewD( parameters_system_operation: dum )
}

以上です。ぜひ動かして試してみてください。