SwiftUI实现日期变化/午夜时实时更新ForEach视图
解决SwiftUI午夜日期变更时自动更新事件列表的问题
核心思路
要实现午夜自动刷新事件列表,需完成两个关键逻辑:
- 计算当前时间到下一个午夜的间隔,设置定时器触发数据刷新
- 监听应用前后台切换,确保回到前台时若日期已变更,同步刷新数据
修改后的ViewModel代码
import Foundation import UIKit @MainActor final class TodaysEventsViewModel: ObservableObject { @Published var events = [EventModel]() @Published var event = [Event]() @Published var selectedEvent: Event? @Published var date = Date() // 静态DateFormatter,避免重复创建提升性能 private static let dateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "dd.MM.yyyy" return formatter }() private var stringDate: String { Self.dateFormatter.string(from: date) } private var midnightTimer: Timer? private var foregroundNotificationObserver: NSObjectProtocol? init() { fetchEvents() getTodaysEvents() setupMidnightTimer() setupForegroundNotification() } deinit { // 清理定时器和通知监听,防止内存泄漏 midnightTimer?.invalidate() if let observer = foregroundNotificationObserver { NotificationCenter.default.removeObserver(observer) } } private func fetchEvents() { let eventsData = Bundle.main.decode([EventModel].self, from: "EventsData.json") events = eventsData } private func getTodaysEvents() { event = events.first(where: {$0.date == stringDate})?.event ?? [] } // 设置午夜触发的定时器 private func setupMidnightTimer() { // 计算下一个午夜的时间 let calendar = Calendar.current var components = calendar.dateComponents([.year, .month, .day], from: Date()) components.day! += 1 components.hour = 0 components.minute = 0 components.second = 0 guard let nextMidnight = calendar.date(from: components) else { return } let timeInterval = nextMidnight.timeIntervalSinceNow // 创建一次性定时器,触发后刷新数据并重新设置次日定时器 midnightTimer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false) { [weak self] _ in self?.date = Date() self?.getTodaysEvents() self?.setupMidnightTimer() } } // 监听应用回到前台的通知 private func setupForegroundNotification() { foregroundNotificationObserver = NotificationCenter.default.addObserver( forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main ) { [weak self] _ in let currentDateString = Self.dateFormatter.string(from: Date()) let storedDateString = Self.dateFormatter.string(from: self?.date ?? Date()) // 日期不一致时刷新数据 if currentDateString != storedDateString { self?.date = Date() self?.getTodaysEvents() self?.setupMidnightTimer() } } } }
代码说明
- 静态DateFormatter:将日期格式化器设为静态属性,避免每次调用
stringDate时重复创建,优化性能。 - 午夜定时器:计算当前到下一个午夜的时间差,设置一次性定时器,触发后更新日期、刷新事件列表,并重新设置次日的定时器,保证每日午夜自动刷新。
- 前台通知监听:应用从后台回到前台时,对比当前日期与ViewModel存储的日期,若不一致则刷新数据,避免后台运行时错过午夜刷新。
- 资源清理:在
deinit中销毁定时器和通知监听,防止内存泄漏。
View代码修正
原View代码中sheet绑定变量存在拼写错误,修正后的代码如下:
import SwiftUI struct TodaysEventsView: View { @StateObject private var viewModel = TodaysEventsViewModel() var body: some View { VStack(spacing: 7.5) { ForEach(viewModel.event, id: \.id) { event in Button { viewModel.selectedEvent = event } label: { EventCell(event: event) } } } .sheet(item: $viewModel.selectedEvent) { event in EventDetailView(event: event) } } }
内容的提问来源于stack exchange,提问作者Gucci
相关产品推荐
相关产品推荐

