如何等待API加载数据后再用CalendarKit创建日历事件
解决SwiftUI+CalendarKit+Combine API加载时机问题
核心思路
把API请求和日历事件展示彻底解耦:ViewModel负责管理数据状态,日历视图只关注从ViewModel读取已加载的数据,当数据更新时自动刷新日历。
1. 重构ViewModel:用@Published管理事件数据
在ViewModel里用@Published属性存储已加载的事件,API请求完成后直接更新这个属性,Combine会自动通知订阅者。
import Combine class CalendarViewModel: ObservableObject { @Published var calendarEvents: [EventModel] = [] // 替换成你的事件模型 private var cancellables = Set<AnyCancellable>() func fetchCalendarEvents() { // 替换成你的真实API调用 APIService.fetchEvents() .receive(on: DispatchQueue.main) .sink(receiveCompletion: { completion in if case .failure(let error) = completion { print("API请求失败:\(error.localizedDescription)") } }, receiveValue: { [weak self] events in self?.calendarEvents = events // 更新已加载的事件 }) .store(in: &cancellables) } }
2. 桥接视图:监听数据变化并刷新日历
在UIViewControllerRepresentable中,订阅ViewModel的calendarEvents变化,一旦数据更新就调用日历的刷新方法,绝对不要在eventsForDate里触发API请求。
import SwiftUI import CalendarKit struct CalendarView: UIViewControllerRepresentable { @ObservedObject var viewModel: CalendarViewModel private var cancellables = Set<AnyCancellable>() func makeUIViewController(context: Context) -> CalendarViewController { let calendarVC = CustomCalendarViewController() calendarVC.viewModel = viewModel // 监听ViewModel的事件变化,触发日历刷新 viewModel.$calendarEvents .sink { [weak calendarVC] _ in calendarVC?.reloadData() } .store(in: &cancellables) // 初始加载数据 viewModel.fetchCalendarEvents() return calendarVC } func updateUIViewController(_ uiViewController: CalendarViewController, context: Context) { // 不要在这里调用fetch,否则会因视图更新触发无限请求 } } // 自定义CalendarViewController子类,持有ViewModel引用 class CustomCalendarViewController: CalendarViewController { var viewModel: CalendarViewModel! override func eventsForDate(_ date: Date) -> [EventDescriptor] { // 直接从ViewModel读取已加载数据,过滤对应日期的事件 viewModel.calendarEvents .filter { event in Calendar.current.isDate(event.startDate, inSameDayAs: date) } .map { event in // 转换为CalendarKit所需的EventDescriptor对象 CalendarEvent( startDate: event.startDate, endDate: event.endDate, title: event.title, color: event.color ) } } }
3. 主视图使用示例
在你的SwiftUI主视图中直接传入ViewModel:
struct MainView: View { @StateObject var viewModel = CalendarViewModel() var body: some View { CalendarView(viewModel: viewModel) .edgesIgnoringSafeArea(.all) } }
关键注意点
- 禁止在
eventsForDate里调用API:这个方法会被CalendarKit频繁触发(滑动日历、视图刷新等场景),会导致重复请求甚至无限循环。 - 用
@ObservedObject/@StateObject管理ViewModel:确保SwiftUI能正确监听数据变化,自动同步桥接视图状态。 - 刷新日历用
reloadData():这是CalendarKit提供的标准方法,会触发重新调用eventsForDate获取最新数据。
内容的提问来源于stack exchange,提问作者XoliKnight
相关产品推荐
相关产品推荐

