The count down in this code has some problem. Do you know how to fix it?
// TemplateView.swift
import SwiftUI
struct TemplateView: View {
@ObservedObject var model = TemplateViewModel()
var body: some View {
VStack {
InfoView(model: model)
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
import Foundation
import SwiftUI
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"),
]
var time: Int {
return data[currentIdx].prepareSeconds
}
func getTime(idx: Int) -> Int {
return data[idx].prepareSeconds
}
func getContent(idx: Int) -> String {
return data[idx].content
}
}
// InfoView.swift
import SwiftUI
struct InfoView: View {
@ObservedObject var model: TemplateViewModel
@State var prepareSeconds: Int = 0
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
init(model: TemplateViewModel) {
self.model = model
self._prepareSeconds = State(initialValue: model.getTime(idx: model.currentIdx))
}
var body: some View {
Text(prepareSeconds.description)
.onReceive(timer, perform: { _ in
if prepareSeconds > 0 {
prepareSeconds -= 1
}
})
}
}
// QuizModel.swift
struct QuizModel {
var prepareSeconds: Int
var content: String
}
Solution 1: onReceive
InfoView(model: model)
.onReceive(model.$currentIdx) { // reset prepareSeconds }
Solution 2: id
InfoView(model: model)
.id(model.currentIdx)
Solution 3: move the timer to the function
struct TemplateView: View {
@ObservedObject var model = TemplateViewModel()
var body: some View {
VStack {
InfoView(model: model)
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
}
model.countDownFrom(seconds: model.getTime(idx: model.currentIdx))
}
}.onAppear {
model.countDownFrom(seconds: model.getTime(idx: model.currentIdx))
}
}
}
class TemplateViewModel: ObservableObject {
@Published var currentIdx: Int = 0
@Published var prepareSeconds: Int = 0
private var cancellable : AnyCancellable?
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"),
]
var time: Int {
return data[currentIdx].prepareSeconds
}
func getTime(idx: Int) -> Int {
return data[idx].prepareSeconds
}
func getContent(idx: Int) -> String {
return data[idx].content
}
func countDownFrom(seconds: Int) {
prepareSeconds = seconds
cancellable = Timer.publish(every: 1, on: .main, in: .common).autoconnect().sink(receiveValue: { (_) in
if self.prepareSeconds > 0 {
self.prepareSeconds -= 1
}
})
}
}
struct InfoView: View {
@ObservedObject var model: TemplateViewModel
var body: some View {
Text(model.prepareSeconds.description)
}
}
struct QuizModel {
var prepareSeconds: Int
var content: String
}