Home > AI > IOS > SwiftUI >

onReceive with ObservableObject

Example: refresh the view with onReceive, without onAppear (which only called once). This example needs to correctly show the preparation seconds for the user to do the specific quiz.

TemplateView.swift

import SwiftUI

struct TemplateView: View {
    @ObservedObject var model = TemplateViewModel()
    
    @State var prepareSeconds: Int = 10
    private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    
    init() {
        self._prepareSeconds = State(initialValue: model.getTime(idx: model.currentIdx))
    }
    


    var body: some View {
        NavigationView {
            VStack {
                Text(prepareSeconds.description)
                    .onReceive(timer) { (time) in
                        prepareSeconds -= 1
                    }
                    .onReceive(model.$currentIdx, perform: { (_) in
                        prepareSeconds = model.getTime(idx: model.currentIdx)
                    })
    
                
                NavigationLink(
                    destination: InfoView(),
                    label: {
                        Text(model.getContent(idx: model.currentIdx))
                            
                    })
                
                Image(systemName: "arrowshape.turn.up.right.fill")
                    .onTapGesture {
                        if model.currentIdx >= model.data.count-1 {
                            model.currentIdx = 0
                        } else {
                            model.currentIdx += 1
                        }
                    }
            }
        }
    }
}

TemplateViewModel.swift

class TemplateViewModel: ObservableObject {
    @Published var currentIdx: Int = 0
    
    var data: [QuizModel] = [
        QuizModel(prepareSeconds: 10, content: "Answer the short question"),
        QuizModel(prepareSeconds: 20, content: "Listen to the audio"),
        QuizModel(prepareSeconds: 30, content: "Read the article"),
    ]
    
    func getTime(idx: Int) -> Int {
       
        return data[idx].prepareSeconds
    }
    
    func getContent(idx: Int) -> String {
        return data[idx].content
    }
    
}

InfoView.swift

struct InfoView: View {
    @Environment(\.presentationMode) var mode

    var body: some View {
        Text("good")
            .onTapGesture {
                mode.wrappedValue.dismiss()
            }
    }
}

Leave a Reply