Основание секундомера на Date() - SwiftUI

Я хочу иметь секундомер в своем приложении, который полностью работает со временем устройства. У меня есть мой код ниже, который принимает время, в течение которого нажата кнопка запуска, а затем каждую секунду обновляет secondsElapsed, чтобы быть разницей между startTime и текущим. Я застреваю в реализации функции паузы. Если я просто аннулирую таймер обновления, то таймер перезапустится, в значительной степени продолжая с того места, где он остановился. Любые идеи о том, как это можно сделать?

    class StopWatchManager: ObservableObject{

    @Published var secondsElapsed = 0
    
    var startTime: Date = Date()
    
    var timer = Timer()
    
    func startWatch(){
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true){ timer in
            let current = Date()
            let diffComponents = Calendar.current.dateComponents([.second], from: self.startTime, to: current)
            let seconds = (diffComponents.second ?? 0)
            self.secondsElapsed = seconds
            
        }
        
    }
    
    func pauseWatch(){
        timer.invalidate()
    }
    
}

Я показываю секундомер, используя этот код ниже:

struct ContentView: View {
    
    @ObservedObject var stopWatchManager = StopWatchManager()
    
    var body: some View{
        HStack{
            Button("Start"){
                stopWatchManager.startWatch()
            }
            Text("\(stopWatchManager.secondsElapsed)")
            Button("Pause"){
                stopWatchManager.pauseWatch()
            }
        }
    }
}
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
308
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Да. Вот как это сделать:

  1. Когда нажата пауза, отметьте текущее время и вычислите прошедшее время для таймера. Недействительный таймер обновления.
  2. Когда таймер возобновится, возьмите текущее время и вычтите прошедшее время. Сделайте это startTime и перезапустите таймер обновления.

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

class StopWatchManager: ObservableObject{
    
    @Published var secondsElapsed = 0
    
    var startTime: Date = Date()
    var elapsedTime = 0.0
    var paused = false
    var running = false
    
    var timer = Timer()
    
    func startWatch(){
        guard !running else { return }
        
        if paused {
            startTime = Date().addingTimeInterval(-elapsedTime)
        } else {
            startTime = Date()
        }
        paused = false
        running = true
        
        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true){ timer in
            let current = Date()
            let diffComponents = Calendar.current.dateComponents([.second], from: self.startTime, to: current)
            let seconds = (diffComponents.second ?? 0)
            self.secondsElapsed = seconds
            
        }
        
    }
    
    func pauseWatch(){
        guard !paused else { return }
        
        timer.invalidate()
        elapsedTime = Date().timeIntervalSince(startTime)
        paused = true
        running = false
    }
    
}

Что следует отметить:

  • Я изменил интервал таймера с 0.1 на 1, чтобы не пропускать обновления.
  • Я добавил переменные состояния paused и running, чтобы кнопки не нажимались более одного раза подряд.

Под возобновлением таймера вы имеете в виду время в момент нажатия, а затем вычитание прошедшего времени? Я думаю, что меня путают с моими именами переменных и вашей формулировкой. Спасибо за помощь!

benpomeroy9 12.12.2020 01:05

Да, возьмите время при нажатии кнопки возобновления и вычтите прошедшее время, чтобы получить новое startTime. У меня есть код, чтобы добавить к моему ответу. Прежде чем я добавлю его, какой кнопкой вы хотите возобновить таймер, Start или Pause? Или, возможно, Pause следует изменить на Unpause или Resume, когда время поставлено на паузу.

vacawama 12.12.2020 01:09

В моем полном приложении кнопка меняется с «Начать просмотр» на «Пауза просмотра» (при нажатии), а затем обратно, чтобы начать просмотр. Поэтому я хотел бы начать, чтобы возобновить его

benpomeroy9 12.12.2020 01:11
let diffComponents = Calendar.current.dateComponents([.second], from: self.startTime, to: current) ?? почему бы и не Date().timeIntervalSince(startTime) или startTime.timeIntervalSinceNow тоже?
Leo Dabus 12.12.2020 07:20
Date().addingTimeInterval(-elapsedTime) также может быть записано как Date(timeIntervalSinceNow: -elapsedTime) или просто Date() - elapsedTime
Leo Dabus 12.12.2020 07:24

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