SwiftUI后台续存计时器开发:时间差计算类型不匹配问题求助
问题解决思路及代码修正
核心错误原因
你遇到的Cannot assign value of type 'DateComponents' to type 'Double'错误,本质是类型不匹配:secondsElapsed是Double类型,而Calendar.current.dateComponents(...)返回的是DateComponents结构体,无法直接赋值。此外代码还有两个隐藏问题:
- 存储与解析时间字符串的格式不统一:存的时候用
.dateTime.hour().minute()(仅时分),但解析时用formatter.timeStyle = .medium(时分秒),会导致解析失败,initialTime始终取默认的Date()。 - 没必要把
Date转成字符串存储,@AppStorage原生支持直接存储Date类型(因为UserDefaults本身支持Date存储)。
修正方案
1. 直接用@AppStorage存储Date(推荐)
先修改View中的代码,直接存储原始Date,避免格式转换的麻烦:
@AppStorage("flightMode_DepTime") private var flightModeDepTime: Date = Date() @ObservedObject var timerManager = TimerFunc() var body: some View { // ... Button { isStandbyMode.toggle() if !isStandbyMode{ flightModeDepTime = Date() // 直接存储当前Date,无需格式化 timerManager.start() }else{ timerManager.pause() } } label: { if isStandbyMode{ Image(systemName: "timer") Text("STBY MODE ") }else{ Image(systemName: "airplane") Text("FLIGHT MODE ") } } // ... }
2. 修正TimerFunc中的时间差计算
修改TimerFunc的代码,直接用存储的Date计算总秒数,赋值给secondsElapsed:
@AppStorage("flightMode_DepTime") private var flightModeDepTime: Date = Date() private var timer: Timer? var mode: TimerMode = .standby // 假设你已定义TimerMode枚举 var secondsElapsed: Double = 0 // 你的Double类型计时变量 func start() { mode = .running timer?.invalidate() // 先销毁旧定时器,避免重复创建 timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in guard let self = self else { return } let now = Date() // 直接计算初始时间到当前时间的时间差(单位:秒) self.secondsElapsed = now.timeIntervalSince(self.flightModeDepTime) } } func pause() { timer?.invalidate() mode = .standby }
3. 如果坚持用字符串存储(不推荐)
若必须用字符串存储,需保证存和取的格式完全一致:
// View中存储时指定统一格式 UserDefaults.standard.set(Date().formatted(.dateTime.hour().minute().second()), forKey: "flightMode_DepTime") // TimerFunc中解析时匹配相同格式 func start() { mode = .running timer?.invalidate() timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in guard let self = self else { return } let formatter = DateFormatter() formatter.dateFormat = "HH:mm:ss" // 和存储的格式严格匹配 guard let initialTime = formatter.date(from: self.flightModeDepTime) else { self.secondsElapsed = 0 return } let now = Date() // 将DateComponents转换为总秒数(Double类型) let components = Calendar.current.dateComponents([.second], from: initialTime, to: now) self.secondsElapsed = Double(components.second ?? 0) } }
额外建议
- 使用
[weak self]避免定时器引发的循环引用。 - 每次启动定时器前调用
timer?.invalidate(),防止创建多个并行定时器。
内容的提问来源于stack exchange,提问作者flyer74
相关产品推荐
相关产品推荐

