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

如何等待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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 22:35:21