よっしーブログ

iOSエンジニアやってます。SwiftUI勉強中です。

@ObservedObjectについてサンプルで理解する

こんにちは。iOSエンジニアをしております。よっしーです。
今回はSwiftUIでよく使用する、@ObservedObjectについてサンプルを実装して理解を深めたいと思います。

はじめに

今回も引き続きSwiftUIのpropertyWrapper編です。
前回は@Bindingについて書きました。

yosshi-ios.hatenablog.com

環境

今回使用している各バージョンは下記です。

・[Xcode] 14.1
・[Swift] 5.7.1
・[iOS] 16.1

@ObservedObjectとは

@ObservedObjectを使用すると、複数のプロパティの変更を監視し、更新された時にビューに表示されている値も更新する為のものです。
@Stateを複数扱う場合に使うってことですね!

@Stateなんじゃそらと思った方は前回書いた記事を御覧ください。

yosshi-ios.hatenablog.com

使い方

  • 対象とするクラスに@ObservableObjectプロトコルを準拠させます。
class GameSoft: ObservableObject {
        
}
  • 監視対象の変数を@Publishedで宣言します。
class GameSoft: ObservableObject {
    @Published var name = "ゼ○ダの伝説"
    @Published var price = 7678
}
struct ContentView: View {
    @ObservedObject var gameSoft = GameSoft() // インスタンス化
        
    var body: some View {
    }
}

サンプル

Text二つとボタンが表示されており、「100円の値上げをする」ボタンを押下すると値の更新によって再描画されすぐに画面に表示されている価格が変更されます。

コードは以下です。

class GameSoft: ObservableObject {
    @Published var name = "ゼ○ダの伝説"
    @Published var price = 7678
}

struct ContentView: View {
    @ObservedObject var gameSoft = GameSoft()
        
    var body: some View {
        
        VStack(spacing: 10) {
            Text("ゲームタイトル: \(gameSoft.name)")
            Text("価格: \(gameSoft.price)")
            
            Button {
                gameSoft.price += 100
            } label: {
                Text("100円の値上げをする")
            }
        }
    }
}

丸ごと別のViewに共有する

インスタンスをそのまま別のViewに共有することができます。
以下のサンプル、子Viewでゲームタイトルを変更し親Viewに戻るとインスタンスが共有されているので、変更されているのが分かります。

子Viewでも共有するインスタンスに@ObservedObjectをつけます。
以下がコードです。

class GameSoft: ObservableObject {
    @Published var name = "ゼ○ダの伝説"
    @Published var price = 7678
}

struct ContentView: View {
    @ObservedObject var gameSoft = GameSoft()
        
    var body: some View {
        
        NavigationView {
            VStack(spacing: 15) {
                Text("ゲームタイトル: \(gameSoft.name)")
                
                NavigationLink {
                    // 子Viewにそのままインスタンスを共有
                    SecoundView(gameSoft: gameSoft)
                } label: {
                    Text("タイトル変更")
                }
            }
        }
    }
}

struct SecoundView: View {
    // 共有するインスタンスにもObservedObjectをつける
    @ObservedObject var gameSoft: GameSoft
    
    var body: some View {
        VStack {
            Text("ゲームタイトル: \(gameSoft.name)")
            TextField("ゲームタイトルを入力", text: $gameSoft.name)
                .textFieldStyle(RoundedBorderTextFieldStyle())
        }
    }
}

一部分Viewに共有する

一部分を共有する場合は、子Viewに@Bindingをつけます。
親Viewの方は、$を付けて参照渡しを行います。
サンプルの挙動は変わらないので、コードだけ記載します。

class GameSoft: ObservableObject {
    @Published var name = "ゼ○ダの伝説"
    @Published var price = 7678
}

struct ContentView: View {
    @ObservedObject var gameSoft = GameSoft()
        
    var body: some View {
        
        NavigationView {
            VStack(spacing: 15) {
                Text("ゲームタイトル: \(gameSoft.name)")
                
                NavigationLink {
                    // 子Viewに$をつけて参照渡し
                    SecoundView(gameSoftName: $gameSoft.name)
                } label: {
                    Text("タイトル変更")
                }
            }
        }
    }
}

struct SecoundView: View {
    // @Bindingをつけて一部分を共有
    @Binding var gameSoftName: String
    
    var body: some View {
        VStack {
            Text("ゲームタイトル: \(gameSoftName)")
            TextField("ゲームタイトルを入力", text: $gameSoftName)
                .textFieldStyle(RoundedBorderTextFieldStyle())
        }
    }
}

まとめ

@ObservedObjectとはデータバインディングの仕組みの一つであり、データクラスの更新を監視するもの。
変更を監視するクラスに、@ObservableObjectプロトコルを準拠させ@Published と組み合わせて使用する。

次回は@StateObjectについて書きたいと思っています。
勉強中ですので、間違っている点等ございましたら、教えていただけると助かります。

参考リンク

Apple Developer Documentation

【Swift UI】@ObservedObjectの意味と使い方!クラスとプロトコルとの関係

【SwiftUI】@ObservedObjectの使い方 | カピ通信