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

Apple Watch SwiftUI 常亮状态下更新日期间隔字符串的最优实现方案

SwiftUI Apple Watch 应用展示数据相对更新时间的最佳实践

场景说明

开发基于SwiftUI的Apple Watch应用时,需要向用户展示数据最后一次更新的距今时长,通常会在ObservableObject类型的store中用@Published修饰Date类型的lastUpdatedDate变量记录最新更新时间。

现有方案的问题

方案1:Text.DateStyle.relative

直接使用系统自带的相对时间样式:

Text(store.lastUpdatedDate, style: .relative)

优势是无需额外编码,系统会自动更新相对间隔。但缺点是格式完全不可自定义,无法隐藏秒数,不能满足仅展示分钟/小时单位的需求。

方案2:计算属性搭配RelativeDateTimeFormatter

通过Date扩展实现相对时间格式化,用计算属性返回字符串:

// 计算属性
private var lastUpdatedText: String {
    store.lastUpdatedDate.timeAgoDisplay()
}

// Date扩展
extension Date {
    func timeAgoDisplay() -> String {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .full
        return formatter.localizedString(for: self, relativeTo: Date())
    }
}

该方案仅在视图其他元素触发刷新时才会重新计算文本内容,无法适配Apple Watch常亮显示、抬腕唤醒的场景,会出现文本内容过时的问题。

不推荐的替代思路

手动维护@Published修饰的格式化字符串、通过ExtensionDelegate生命周期方法触发更新,都只能覆盖应用回到前台的场景,无法适配常亮显示需求;自定义Timer定时更新的方案需要手动处理前后台启停逻辑,代码冗余且容易出现内存泄漏,性能开销更高。

最优解决方案:使用TimelineView实现自动定时刷新

SwiftUI自带的TimelineView可以按照指定的时间规则自动刷新视图,无需手动维护Timer,系统会自动管理刷新时机,性能开销极低,完美适配watchOS常亮、抬腕唤醒场景。

实现步骤

  1. 给Date添加扩展,对齐最后更新时间的秒数,保证文本在更新时间过整分钟时立刻刷新,实现分钟级的精确展示:
extension Date {
    func withSameSeconds(asDate date: Date) -> Date? {
        let calendar = Calendar.current
        var components = calendar.dateComponents([.era, .year, .month, .day, .hour, .minute, .second], from: self)
        let secondsDateComponents = calendar.dateComponents([.era, .year, .month, .day, .hour, .minute, .second], from: date)
        
        components.second = secondsDateComponents.second
        
        return calendar.date(from: components)
    }
}
  1. 封装独立的展示视图,使用PeriodicTimelineSchedule指定每分钟刷新一次,同时可以优化格式化方法,通过allowedUnits隐藏秒数:
// 优化后的格式化扩展
extension Date {
    func timeAgoDisplay() -> String {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .full
        // 仅展示小时、分钟单位,隐藏秒
        formatter.allowedUnits = [.hour, .minute]
        return formatter.localizedString(for: self, relativeTo: Date())
    }
}

// 相对时间展示视图
struct LastUpdatedTextView: View {
    @ObservedObject var store: DataStore
    
    var body: some View {
        if let nowWithSameSeconds = Date().withSameSeconds(asDate: store.lastUpdatedDate) {
            TimelineView(PeriodicTimelineSchedule(from: nowWithSameSeconds, by: 60)) { context in
                Text(store.lastUpdatedDate.timeAgoDisplay())
            }
        } else {
            EmptyView()
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 13:09:04