Home > AI > IOS > SwiftUI >

how to refresh the count down

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
}

Leave a Reply