【SwiftUI】Scene間のEnvironmentObjectの共有・専有(ObservedObject 、StateObject)

SwiftUI

iPadOSなどでは1つのアプリで同時に複数のウィンドウを開くことが出来ます。
このウィンドウ毎にSceneが割り当てられいています。
Sceneが複数ある場合はEnvironmentObjectの扱いに気をつける必要があります。

EnvironmentObjectの共有

ObservedObjectの変数で持っているインスタンスをenvironmentObjectに渡すと、
全てのSceneでインスタンスが共有されます。

設定などのパラメータを共有する際には便利ですが、
sheetやalertの表示に使用してしまうと全てのSceneで同時に表示されます。

EnvironmentObjectの専有

StateObjectの変数で持っているインスタンスをenvironmentObjectに渡すと、
Scene毎に独立したインスタンスとなります。
また、変数として持たずに直接environmentObjectに渡した場合も同様です。

sheetやalertの表示やScene毎に個別に持ちたいパラメータは、
こちらの方法で使用するようにしましょう。

サンプルコードと検証

class SheetModel:ObservableObject{
    @Published var sheet = false
}

struct ContentView: View {
    
    @ObservedObject var obsSheetModel = SheetModel()
    @StateObject var stateSheetModel = SheetModel()
    
    var body: some View {
        VStack {
            ChildView(label: "ObservedObject")
                .environmentObject(obsSheetModel)
            ChildView(label: "StateObject")
                .environmentObject(stateSheetModel)
            ChildView(label: "Instance")
                .environmentObject(SheetModel())
        }
    }
}

struct ChildView:View{
    
    @EnvironmentObject var sheetModel:SheetModel
    let label:String
    
    var body: some View{
        Button(label){
            sheetModel.sheet.toggle()
        }
        .padding()
        .sheet(isPresented: $sheetModel.sheet) {
            Text(label)
        }
    }
    
}

Sheetを表示するだけの簡単なコードです。
しかし問題点もわかりやすいと思います。

ObservedObjectで使用した時は左右が連動してしまいました。
Sheetの様な場合は両方に表示する必要がないので気をつけて使いましょう。

最後に

iOSアプリでStateObjectとObservedObjectを比較している記事をよく見ますが、
大きく異なるのはiPadOSなどのマルチウィンドウの場合です。

iPadOSをリリースする場合はEnable Multiple Windowはデフォルトでオンになっています。
iOS向けで開発してついでにiPadOSにもリリースすると、
とりあえずObservedObjectを使ってしまっている場合もあると思うので気をつけましょう。

コメント

タイトルとURLをコピーしました