見出し画像

[SwiftUI]SwiftUIのデータクラスの更新監視@ObservedObject

SwiftUIにはデータクラスの更新を監視する仕組みとしてObservedObjectというものがある。今回はこれについて記載する。

1. @Stateとの違いは?

@StateはViewのプロパティが更新された際にViewの再描画を行うものだが、@ObservedObject はデータクラス(後述するObservableObjectプロトコルに準拠)のプロパティ(後述する@Publishedが付与されたプロパティ)の更新を監視し、更新されたらViewを再描画するという仕組み。

2. サンプルコード

struct ContentView: View {
    
    @ObservedObject var quizModel = QuizModel()

    var body: some View {
        VStack {
            Text("第\(quizModel.quizNumber)問")
                .font(.largeTitle)
                .padding(.bottom, 30)
            Text(quizModel.currentQuiz.text)
                .font(.title)
            HStack {
                Button {
                    quizModel.answerQuiz(playAnswer: true)
                } label: {
                    CircleView()
                }
                .padding()
                Button {
                    quizModel.answerQuiz(playAnswer: false)
                } label: {
                    CrossView()
                }
                .padding()

            }
            .padding()
            
            Text("\(quizModel.collectCount)問正解")
                .font(.body)
        }
        .padding()
        .onAppear {
            quizModel.fetchQuiz()
        }
    }
}

struct CircleView: View {
    var body: some View {
        Circle()
            .stroke(Color.blue, lineWidth: 20)
            .frame(width: 150, height: 150)
    }
}

struct CrossView: View {
    var body: some View {
        Path { path in
                    path.move(to: CGPoint(x: 0, y: 0))
                    path.addLine(to: CGPoint(x: 150, y: 150))
                    path.move(to: CGPoint(x: 150, y: 0))
                    path.addLine(to: CGPoint(x: 0, y: 150))
                }
                .stroke(lineWidth: 20)
                .fill(Color.red)
                .frame(width: 150, height: 150)
    }
}

class QuizModel: ObservableObject {
    
    @Published var quizNumber = 0
    @Published var currentQuiz = Quiz(text: "Dummy", answer: true)
    @Published var collectCount = 0
    
    let quizList = [
        Quiz(text: "1+1=2?", answer: true),
        Quiz(text: "3-5=2?", answer: false),
        Quiz(text: "10/5=2?", answer: true),
    ]
    
    func answerQuiz(playAnswer answer: Bool){
        if currentQuiz.answer == answer {
            collectCount += 1
        }
        fetchQuiz()
    }
    
    func fetchQuiz() {
        quizNumber += 1
        
        let nextQuiz = quizList.randomElement()
        currentQuiz = nextQuiz!
    }
}

struct Quiz {
    let text: String
    let answer: Bool
}

3. ソースコードの説明

簡単な算数の○×クイズです。
○または×をタップするとランダムで次の問題に進むといったもの。
クイズの進行管理はContentViewのプロパティquizModelが行っており、次のクイズなどをViewに通知したいため@ObservedObjectにしている。
このとき、上述したが@ObservedObjectのデータクラスはObservableObjectプロトコルに準拠したクラスであり、Viewに通知したいプロパティに@Publishedを付与する必要がある。

4. 終わりに

@Stateや@ObservedObjectのような仕組みのものをデータバインディングという。他にも@EnvironmentObjectといったものもあったりして、それぞれ用途が違うので場面に合わせて使い分ける。

いいなと思ったら応援しよう!