简体   繁体   中英

Swift 5: ObservedObject Not updating

I've created an ObservableObject class called ActiveQuiz with info I want to share across multiple views:

import Foundation

class ActiveQuiz: ObservableObject{
    @Published var iterator = 0
    @Published var questions = createQuiz()
}

In one of the views I display text, that if tapped on will increase the iterator which in turn updates the text as it has moved on to the next entry in the questions array:

import SwiftUI

struct Question: View {
    @ObservedObject var activeQuiz = ActiveQuiz()

    var body: some View {
        Text(self.activeQuiz.questions[self.activeQuiz.iterator].countryInfo.capital)
            .font(.title)
            .onTapGesture(perform: {
                // increase iterator by 1 unless it's at the end, then put back to 0
                self.activeQuiz.iterator = self.activeQuiz.iterator + 1 == self.activeQuiz.questions.count ? 0 : self.activeQuiz.iterator + 1
            } )
    }
}

In the other view I have a count which displays where the iterator is:

import SwiftUI

struct QuestionCounter: View {
    @ObservedObject var activeQuiz = ActiveQuiz()

    var body: some View {
        Text(String(self.activeQuiz.iterator + 1) + "/" + String(self.activeQuiz.questions.count))
    }
}

The text from the first view is updating as expected but the iterator from the second view stays at the initial 1/x.

Question : Being new the Swift, I don't understand why the shared state of the ObservableObject ActiveQuiz isn't updating across all view that are "observing" it. Also in being new to swift, I apologize if I've not included any info needed to answer the question that I'm not aware of.

It's not clear, how you use these views together, maybe for others it will be helpful to add ContentView , for example. As I see, you use 2 different ActiveQuiz instances. So your Question struct have 1 instance and you update it's iterator and QuestionCounter has it's own instance, which is not updating from the first one. I think the solution should be to use @EnvironmentObject for both structs, like this (I simplified your example because of lack of code):

import SwiftUI
import Combine

class ActiveQuiz: ObservableObject {
    @Published var iterator = 0
}

struct UsingEnvironmentObject: View {

    @EnvironmentObject var activeQuiz: ActiveQuiz
    var body: some View {
        VStack {

            Question() // it will take EnvironmentObject from parent
            QuestionCounter() // this one will take the same EnvironmentObject from parent

        }
    }
}

// more representative solution
struct UsingEnvironmentObject: View {

    @ObservedObject var activeQuiz = ActiveQuiz()

    var body: some View {
        VStack {
            // here you see, that both structs use one instance
            Question()
                .environmentObject(activeQuiz)
            QuestionCounter()
                .environmentObject(activeQuiz)

        }
    }
}

struct Question: View {

    @EnvironmentObject var activeQuiz: ActiveQuiz

    var body: some View {
        Text("some text from question")
            .font(.title)
            .onTapGesture(perform: {
                // increase iterator by 1 unless it's at the end, then put back to 0
                self.activeQuiz.iterator = self.activeQuiz.iterator + 1 == 10 ? 0 : self.activeQuiz.iterator + 1
            } )
    }
}

struct QuestionCounter: View {
    @EnvironmentObject var activeQuiz: ActiveQuiz

    var body: some View {
        Text(String(self.activeQuiz.iterator + 1) + "/10")
    }
}



struct UsingEnvironmentObject_Previews: PreviewProvider {
    static var previews: some View {
        UsingEnvironmentObject()
        .environmentObject(ActiveQuiz())
    }
}

and you will achieve this: 在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM