我正在嘗試在 swiftui 中制作一個計時器,但每當我運行它時,它都會自動將倒計時增加 30 分鐘。例如,當我將倒計時時間設定為 5 分鐘并單擊“開始”按鈕時,它會顯示為 35 分鐘,但當我再次單擊該按鈕時,它會一直切換到隨機時間。以上是它將切換到的隨機時間。
我從 Indently 在 youtube 上的一個教程中得到了這個計時器,但改變了一些東西以適應我想要它做的事情。我嘗試設定一個自定義時間,以便計時器始終從 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:d", 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()
}
}
uj5u.com熱心網友回復:
我注意到并修復了您代碼中的一些問題:
start()正在使用 的當前值呼叫vm.minutes,因此它將從該值而不是 開始5。我將其更改為 useself.initialTime,這意味著它當前未使用傳入的值。您需要決定是否start()真的要獲取一個值以及如何使用它。reset()沒有被呼叫。我稱它為start().暫停只是暫停螢屏更新。我更改它以跟蹤暫停的開始時間并計算暫停的時間量,以便它可以準確地更新顯示的時間。
我將暫停/恢復按鈕設為一個按鈕,標題和顏色的條件值基于
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:d", 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()
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/536716.html
標籤:迅速迅捷定时器
上一篇:實體成員“$isBrowsingWebsite”不能用于型別“MapPinSheetSectionOneView_Previews”
