SwiftUI入門-勉強メモ008-【プロパティ共有その2 ObservedObject】

今回は自分で作ったクラスの値をViewで利用したい場合の方法です.

環境は,

  • macOS Catalina 10.15.7
  • Xcode 12.0.1
  • Swift 5.3
  • iOS 14.0

です.

目次

ObservedObjectとは

Stateのところでも話したようにViewは構造体のため, 基本的に値の変更や更新ができません. そこでView内のプロパティの変更を可能にするのが@Stateというプロパティラッパーでした.

ただ@StateはView内でした使うことができませんので, 自分で作成したクラスの値を利用したい場合は用いることができません.

そこで登場するのが@ObservedObjectです.

@ObservedObjectは,

@Stateの役割
  • クラスで設定された値の更新が可能になる
  • SwifuUIがプロパティの管理を行い, 値が更新されるたびにViewを再表示する

ようになります. ここまでは@Stateと変わりありません.

この@ObservedObjectは,

  1. クラスを定義し, その中の複数のプロパティを管理することができる.
  2. クラスで定義するため, 複数のViewで参照することができる.
  3. 作成するクラスは「ObservableObjectプロトコル」に準拠しなければならない.
  4. クラスの中で定義したプロパティをSwiftUIの管理対象に指定するためには@Publishedをつける.
  5. クラスでは, 管理対象ではないプロパティやメソッドを定義することができる.
  6. クラスのインスタンス作成などは, SwiftUIのView側の役割となる.

と注意するべきで点もあります. 具体的な例を見ていきます.

基本的な例

@Stateで処理できるもののコードを書き直しました. Numberクラスを作成してその中でカウントした数を保存するプロパティを作成します.

SwiftUI
SwiftUI
import SwiftUI

/// Numberクラスの定義
class Number: ObservableObject {
    //回数をカウントするプロパティの設定
    @Published var num = 0
}

struct ContentView: View {
    
    //インスタンスを生成
    @ObservedObject var number = Number()
    
    var body: some View {
        VStack(spacing: 20.0) {
            
            //ボタンの作成
            Button(action: {
                //ボタンが押されるたびに+1していく
                number.num += 1
            }) {
                Text("ボタンを押してください")
                    .fontWeight(.bold)
                    .foregroundColor(Color.blue)
                    .padding(10)
            }
            .background(Color.yellow)
            .border(Color.blue)
            
            //ボタンを押された回数を表示
            Text("\(number.num)")
                .font(.largeTitle)
        }
        .font(.title)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

ただこれは同じView内で@Stateを用いてプロパティを作成すればよいので, @ObservedObjectを用いるまでもありません.

SubViewでの値の引き渡し

今度は複数のところから値を参照してみます. 完成形は下のようなもので, ボタンを押す度に2ヶ所で値の更新をします.

import SwiftUI

// Numberクラスの定義
class Number: ObservableObject {
    //回数をカウントするプロパティの設定
    @Published var num = 0
}

struct ContentView: View {
    
    //インスタンスを生成
    @ObservedObject var number = Number()
    
    var body: some View {
        VStack(spacing: 20.0) {
            
            //ボタンの作成
            Button(action: {
                //ボタンが押されるたびに+1していく
                number.num += 1
            }) {
                Text("ボタンを押してください")
                    .fontWeight(.bold)
                    .foregroundColor(Color.blue)
            }
            .padding(10)
            .background(Color.yellow)
            .border(Color.blue)
            
            //ボタンを押された回数を表示
            Text("\(number.num)")
                .font(.largeTitle)
            
            //SubViewの作成
            SubView(n: number)
        }
        .font(.title)
    }
}

//SubViewの定義
struct SubView:View {
    @ObservedObject var n : Number
    var body: some View {
        VStack {
            Text("ここにも \(n.num) が出力されます")
        }
        .padding(10)
        .background(Color.pink)
    }
}
SwiftUI
  1. Numberのクラス
    ここで回数をカウントするプロパティ「Number」を設定しています.
    SwiftUIに管理してもらうために@Publishedをつけています.
    クラス自体は「ObservableObjectプロトコル」に準拠しています
  2. 全体のView
  3. SubViewの作成
    「ここにも〇〇が出力されます」という表示するための仕組みを担っています.
    プロパティは@ObservedObjectでラップします.
  4. ここでクラスのインスタンスを作成をします. @ObservedObjectでラップします.
  5. ボタンが押されたときにカウント数を増やし, num数を更新, 画面の再描写を行います.
  6. ここでボタンが押された回数を表示しています.
  7. SubViewの表示をここで行います.

@Stateでは「$」をつけて値を引き渡していましたが, @ObservedObjectでは不要です.

複数のSubViewでの値の引き渡し

以下のように複数のSubViewでも簡単に管理できます.

import SwiftUI

// Numberクラスの定義
class Number: ObservableObject {
    //回数をカウントするプロパティの設定
    @Published var num = 0
}

struct ContentView: View {
    
    //インスタンスを生成
    @ObservedObject var number = Number()
    
    var body: some View {
        VStack(spacing: 20.0) {
            
            //ボタンの作成
            Button(action: {
                //ボタンが押されるたびに+1していく
                number.num += 1
            }) {
                Text("ボタンを押してください")
                    .fontWeight(.bold)
                    .foregroundColor(Color.blue)
                    .padding(10)
            }
            .background(Color.yellow)
            .border(Color.blue)
            .padding(.bottom, 60)
            
            //SubView2の作成
            SubView2(n: number)
            //SubView3の作成
            SubView3(n: number)
            //SubView4の作成
            SubView4(n: number)
        }
        .font(.title)
    }
}

//SubView2の定義
struct SubView2:View {
    @ObservedObject var n : Number
    var body: some View {
        VStack {
            Text("カウント数を2倍します:\(n.num*2) ")
        }
        .padding(10)
        .background(Color.red)
    }
}

//SubView3の定義
struct SubView3:View {
    @ObservedObject var n : Number
    var body: some View {
        VStack {
            Text("カウント数を3倍します:\(n.num*3) ")
        }
        .padding(10)
        .background(Color.blue)
    }
}

//SubView4の定義
struct SubView4:View {
    @ObservedObject var n : Number
    var body: some View {
        VStack {
            Text("カウント数を4倍します:\(n.num*4) ")
        }
        .padding(10)
        .background(Color.green)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
SwiftUI

孫View(SubViewの中にもう一つSubViewがある状態)の場合

import SwiftUI

// Numberクラスの定義
class Number: ObservableObject {
    //回数をカウントするプロパティの設定
    @Published var num = 0
}

struct ContentView: View {
    
    //インスタンスを生成
    @ObservedObject var number = Number()
    
    var body: some View {
        VStack(spacing: 20.0) {
            
            //ボタンの作成
            Button(action: {
                //ボタンが押されるたびに+1していく
                number.num += 1
            }) {
                Text("ボタンを押してください")
                    .fontWeight(.bold)
                    .foregroundColor(Color.blue)
                    .padding(10)
            }
            .background(Color.yellow)
            .border(Color.blue)
            .padding(.bottom, 30)
            
            //SubView1の作成
            SubView1(n1: number)
        }
        .font(.title2)
    }
}

//SubView1の定義
struct SubView1:View {
    @ObservedObject var n1 : Number
    var body: some View {
        VStack {
            SubView2(n2: n1)
        }
        .padding(15)
        .background(Color.blue)
    }
}

//SubView2の定義
struct SubView2:View {
    @ObservedObject var n2 : Number
    var body: some View {
        VStack {
            Text("カウント数がここに表示されます:\(n2.num) ")
        }
        .padding(10)
        .background(Color.pink)
    }
}
SwiftUI
よかったらシェアしてね!

コメント

コメントする

目次
閉じる