SwiftUI-таймер автоматически добавляет 30 минут к обратному отсчету

Я пытаюсь сделать таймер в swiftui, но всякий раз, когда я его запускаю, он автоматически добавляет 30 минут к обратному отсчету. Например, когда я устанавливаю время обратного отсчета на 5 минут и нажимаю кнопку «Пуск», вместо этого оно будет отображаться как 35 минут, но когда я снова нажимаю кнопку, оно просто продолжает переключаться на случайное время. Выше указано случайное время, на которое он переключится.SwiftUI-таймер автоматически добавляет 30 минут к обратному отсчету

Я получил этот таймер из учебника на YouTube от Indently, но изменил некоторые вещи, чтобы они соответствовали тому, что я хотел. Я попытался установить пользовательское время, чтобы таймер всегда отсчитывал от 5 минут. Насколько я понимаю, таймер работает, беря разницу между текущей датой и датой окончания, а затем используя разницу во времени в качестве обратного отсчета. Ниже приведен код для TimerStruct (ViewModel) и TimerView.

ТаймерСтруктура:

import Foundation

extension TimerView {
    final class ViewModel: ObservableObject {
        @Published var isActive = false
        @Published var showingAlert = false
        @Published var time: String = "5:00"
        @Published var minutes: Float = 5.0 {
            didSet {
                self.time = "\(Int(minutes)):00"
            }
        }
         var initialTime = 0
         var endDate = Date()
        
        // Start the timer with the given amount of minutes
        func start(minutes: Float) {
            self.initialTime = 5
            self.endDate = Date()
            self.isActive = true
            self.endDate = Calendar.current.date(byAdding: .minute, value: Int(minutes), to: endDate)!
        }
        
        // Reset the timer
        func reset() {
            
            self.minutes = Float(initialTime)
            self.isActive = false
            self.time = "\(Int(minutes)):00"
        }
        
        // Show updates of the timer
        func updateCountdown(){
            guard isActive else { return }
            
            // Gets the current date and makes the time difference calculation
            let now = Date()
            let diff = endDate.timeIntervalSince1970 - now.timeIntervalSince1970
            
            // Checks that the countdown is not <= 0
            if diff <= 0 {
                self.isActive = false
                self.time = "0:00"
                self.showingAlert = true
                return
            }
            
            // Turns the time difference calculation into sensible data and formats it
            let date = Date(timeIntervalSince1970: diff)
            let calendar = Calendar.current
            let minutes = calendar.component(.minute, from: date)
            let seconds = calendar.component(.second, from: date)

            // Updates the time string with the formatted time
            self.minutes = Float(minutes)
            self.time = String(format:"%d:%02d", minutes, seconds)
        }
    }
}

ТаймерВид:

import SwiftUI

struct TimerView: View {
    @ObservedObject var vm = ViewModel()
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    let width: Double = 250
    
    var body: some View {
        VStack {
            
            Text("Timer: \(vm.time)")
                .font(.system(size: 50, weight: .medium, design: .rounded))
                .alert("Timer done!", isPresented: $vm.showingAlert) {
                    Button("Continue", role: .cancel) {
                        
                    }
                }
                .padding()
            
            HStack(spacing:50) {
                Button("Start") {
                    vm.start(minutes: Float(vm.minutes))
                }
                .padding()
                .background((Color(red: 184/255, green: 243/255, blue: 255/255)))
                .foregroundColor(.black)
                .cornerRadius(10)
                .font(Font.system(size: UIFontMetrics.default.scaledValue(for: 16)))
                //.disabled(vm.isActive)
                
                if vm.isActive == true {
                    Button("Pause") {
                        vm.isActive = false
                        //self.timer.upstream.connect().cancel()
                    }
                    .padding()
                    .foregroundColor(.black)
                    .background(.red)
                    .cornerRadius(10)
                    .font(Font.system(size: UIFontMetrics.default.scaledValue(for: 16)))
                } else {
                    Button("Resume") {
                        vm.isActive = true
                    }
                    .padding()
                    .foregroundColor(.black)
                    .background(.green)
                    .cornerRadius(10)
                    .font(Font.system(size: UIFontMetrics.default.scaledValue(for: 16)))
                }
            }
            .frame(width: width)
            }
            .onReceive(timer) { _ in
                vm.updateCountdown()
            }
            
        }
    }


struct TimerView_Previews: PreviewProvider {
    static var previews: some View {
        TimerView()
    }
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
137
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я заметил и исправил ряд вещей в вашем коде:

  1. start() вызывается с текущим значением vm.minutes, поэтому он будет начинаться с этого значения, а не 5. Я изменил его на использование self.initialTime, что означает, что в настоящее время он не использует переданное значение. Вам нужно решить, действительно ли start() хочет принять значение и как его использовать.

  2. reset() не звонили. Я называю это от start().

  3. Пауза только приостанавливала обновление экрана. Я изменил его, чтобы отслеживать время начала паузы и вычислять время паузы, чтобы он мог точно обновлять отображаемое время.

  4. Я сделал кнопку «Пауза/Возобновление» одной кнопкой с условными значениями заголовка и цвета на основе vm.active.


Вот обновленный код:

extension TimerView {
    final class ViewModel: ObservableObject {
        @Published var isActive = false
        @Published var showingAlert = false
        @Published var time: String = "5:00"
        @Published var minutes: Float = 5.0 {
            didSet {
                self.time = "\(Int(minutes)):00"
            }
        }
        var initialTime = 0
        var endDate = Date()
        var pauseDate = Date()
        var pauseInterval = 0.0
        
        // Start the timer with the given amount of minutes
        func start(minutes: Float) {
            self.initialTime = 5
            self.reset()
            self.endDate = Date()
            self.endDate = Calendar.current.date(byAdding: .minute, value: self.initialTime, to: endDate)!
            self.isActive = true
        }
        
        // Reset the timer
        func reset() {
            self.isActive = false
            self.pauseInterval = 0.0
            self.minutes = Float(initialTime)
            self.time = "\(Int(minutes)):00"
        }
        
        func pause() {
            if self.isActive {
                pauseDate = Date()
            } else {
                // keep track of the total time we're paused
                pauseInterval += Date().timeIntervalSince(pauseDate)
            }
            self.isActive.toggle()
        }
        
        // Show updates of the timer
        func updateCountdown(){
            guard isActive else { return }
            
            // Gets the current date and makes the time difference calculation
            let now = Date()
            let diff = endDate.timeIntervalSince1970 + self.pauseInterval - now.timeIntervalSince1970
            
            // Checks that the countdown is not <= 0
            if diff <= 0 {
                self.isActive = false
                self.time = "0:00"
                self.showingAlert = true
                return
            }
            
            // Turns the time difference calculation into sensible data and formats it
            let date = Date(timeIntervalSince1970: diff)
            let calendar = Calendar.current
            let minutes = calendar.component(.minute, from: date)
            let seconds = calendar.component(.second, from: date)
            
            // Updates the time string with the formatted time
            //self.minutes = Float(minutes)
            self.time = String(format:"%d:%02d", minutes, seconds)
        }
    }
}

struct TimerView: View {
    @ObservedObject var vm = ViewModel()
    let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
    let width: Double = 250
    
    var body: some View {
        VStack {
            
            Text("Timer: \(vm.time)")
                .font(.system(size: 50, weight: .medium, design: .rounded))
                .alert("Timer done!", isPresented: $vm.showingAlert) {
                    Button("Continue", role: .cancel) {
                        
                    }
                }
                .padding()
            
            HStack(spacing:50) {
                Button("Start") {
                    vm.start(minutes: Float(vm.minutes))
                }
                .padding()
                .background((Color(red: 184/255, green: 243/255, blue: 255/255)))
                .foregroundColor(.black)
                .cornerRadius(10)
                .font(Font.system(size: UIFontMetrics.default.scaledValue(for: 16)))
                //.disabled(vm.isActive)
                
                Button(vm.isActive ? "Pause" : "Resume") {
                    vm.pause()
                    //vm.isActive = false
                    //self.timer.upstream.connect().cancel()
                }
                .padding()
                .foregroundColor(.black)
                .background(vm.isActive ? .red : .green)
                .cornerRadius(10)
                .font(Font.system(size: UIFontMetrics.default.scaledValue(for: 16)))
                
            }
            .frame(width: width)
        }
        .onReceive(timer) { _ in
            vm.updateCountdown()
        }
        
    }
}

Я только что заметил, что если вы нажмете «Возобновить» после первого запуска приложения, вы получите странный результат. Это связано с тем, что код предполагает, что таймер приостановлен, но endDate еще не установлен должным образом. Вы можете исправить это, установив var endDate = Calendar.current.date(byAdding: .minute, value: 5, to: Date())!, или вы можете отключить кнопку «Возобновить», если «Пуск» никогда не нажимался.

vacawama 19.11.2022 18:30

Спасибо! Вам удалось заставить мой код работать намного лучше, но я все же заметил, что таймер по-прежнему добавляет дополнительные 30 минут, когда я запускаю его, хотя время обратного отсчета больше не прыгает как сумасшедшее.

Zudoturiku 19.11.2022 18:59

Я не вижу дополнительных 30 минут с кодом, который я разместил.

vacawama 19.11.2022 19:23

ах, я вижу, я пытался запустить его на своем телефоне, и я все еще вижу проблему. Может быть, я могу отправить вам видео проблемы?

Zudoturiku 19.11.2022 19:34

Выложите видео куда-нибудь, а сюда дайте ссылку.

vacawama 19.11.2022 19:40

Вот ссылка на видео: drive.google.com/drive/folders/…

Zudoturiku 20.11.2022 05:33

Пожалуйста, сделайте доступ общедоступным.

vacawama 20.11.2022 12:42

О, извините за это. Теперь это общедоступно.

Zudoturiku 20.11.2022 15:25

Создайте новый проект и используйте код, который я разместил. Можете ли вы воспроизвести проблему с этим кодом? Если нет, то в вашем коде что-то другое.

vacawama 20.11.2022 23:25

Другие вопросы по теме