今回は自分で作ったクラスの値を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
は,
- クラスを定義し, その中の複数のプロパティを管理することができる.
- クラスで定義するため, 複数のViewで参照することができる.
- 作成するクラスは「ObservableObjectプロトコル」に準拠しなければならない.
- クラスの中で定義したプロパティをSwiftUIの管理対象に指定するためには
@Published
をつける. - クラスでは, 管理対象ではないプロパティやメソッドを定義することができる.
- クラスのインスタンス作成などは, SwiftUIのView側の役割となる.
と注意するべきで点もあります. 具体的な例を見ていきます.
基本的な例
@State
で処理できるもののコードを書き直しました. Numberクラスを作成してその中でカウントした数を保存するプロパティを作成します.


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)
}
}

- Numberのクラス
ここで回数をカウントするプロパティ「Number」を設定しています.
SwiftUIに管理してもらうために@Published
をつけています.
クラス自体は「ObservableObjectプロトコル」に準拠しています - 全体のView
- SubViewの作成
「ここにも〇〇が出力されます」という表示するための仕組みを担っています.
プロパティは@ObservedObject
でラップします. - ここでクラスのインスタンスを作成をします.
@ObservedObject
でラップします. - ボタンが押されたときにカウント数を増やし, num数を更新, 画面の再描写を行います.
- ここでボタンが押された回数を表示しています.
- 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()
}
}

孫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)
}
}

コメント