You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中如何实现相对时间的实时更新?

解决SwiftUI中相对时间实时更新的问题

嘿,这个问题我做项目时也碰到过!核心原因是你当前的代码只在视图初始化时计算了一次相对时间,之后没有任何触发视图刷新的机制,所以时间就固定住了。下面给你两种简单又靠谱的实现方案:

方案一:使用ObservableObject + 定时器(推荐,逻辑更清晰)

我们可以把时间相关的逻辑封装到一个ViewModel里,通过定时器定时更新当前时间,利用@Published属性触发视图刷新:

import SwiftUI

class RelativeTimeViewModel: ObservableObject {
    let timestamp: Double
    // 用@Published标记,更新时会自动通知视图刷新
    @Published private var currentDate = Date()
    
    init(timestamp: Double) {
        self.timestamp = timestamp
        // 设置每秒更新一次当前时间的定时器
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
            self?.currentDate = Date()
        }
    }
    
    // 计算属性,每次访问都会基于最新的currentDate生成相对时间
    var relativeTimeString: String {
        Date(timeIntervalSince1970: timestamp)
            .relativeTime(relativeTo: currentDate)
    }
}

// 扩展Date,让它支持传入参考时间
extension Date {
    func relativeTime(in locale: Locale = .current, relativeTo referenceDate: Date) -> String {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .full
        return formatter.localizedString(for: self, relativeTo: referenceDate)
    }
}

// 修改你的ContentView
struct ContentView: View {
    // 用@StateObject持有ViewModel,确保生命周期和视图一致
    @StateObject private var viewModel = RelativeTimeViewModel(timestamp: 1589014112)
    
    var body: some View {
        Text(viewModel.relativeTimeString)
    }
}

为什么这个方案好用?

  • 把时间逻辑和视图分离,代码更易维护
  • @Published属性自动处理视图刷新,不用手动调用objectWillChange
  • 定时器用[weak self]避免了内存泄漏问题

方案二:直接在视图中使用Timer.publish

如果你的场景比较简单,也可以不用ViewModel,直接在视图里通过Timer.publish来监听时间变化:

import SwiftUI

struct ContentView: View {
    let timestamp: Double = 1589014112
    @State private var currentDate = Date()
    
    var body: some View {
        Text(Date(timeIntervalSince1970: timestamp)
                .relativeTime(relativeTo: currentDate))
        // 订阅每秒发送的时间事件,更新currentDate触发视图刷新
            .onReceive(Timer.publish(every: 1, on: .main, in: .common).autoconnect()) { newDate in
                currentDate = newDate
            }
    }
}

// 同样需要修改Date扩展
extension Date {
    func relativeTime(in locale: Locale = .current, relativeTo referenceDate: Date) -> String {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .full
        return formatter.localizedString(for: self, relativeTo: referenceDate)
    }
}

额外小提示

  • 如果你的相对时间不需要精确到秒(比如只需要显示“几分钟前”“几小时前”),可以把定时器的时间间隔改成60秒,减少不必要的视图刷新,提升性能
  • 上面两种方案都不需要手动取消定时器:方案一的定时器会随着ViewModel的销毁自动停止,方案二的autoconnect()也会在视图消失时自动取消订阅

内容的提问来源于stack exchange,提问作者Mohammed Imthathullah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 16:22:58