FSCalendar 源码解析(一):整体架构与核心组件深度剖析
2026/9/19 13:17:21 网站建设 项目流程

FSCalendar 源码解析(一):整体架构与核心组件深度剖析

【免费下载链接】FSCalendarA fully customizable iOS calendar library, compatible with Objective-C and Swift项目地址: https://gitcode.com/gh_mirrors/fs/FSCalendar

FSCalendar 是一款高度可定制的 iOS 日历控件库,同时支持 Objective-C 与 Swift,被超过 10,000 个 App 采用。本文带你深入 FSCalendar 源码,剖析它的整体架构、核心组件职责与经典设计模式,帮助你在 10 分钟内看懂这个日历库"怎么搭起来的"。

📌核心关键词:FSCalendar 源码解析、iOS 日历控件架构、CalendarView 核心组件


🧭 一分钟了解 FSCalendar

FSCalendar 本质上是一个"伪装成 UIView 的日历容器",它内部组合了以下5 类核心组件

组件文件一句话职责
主控制器FSCalendar.m组装视图树、处理选择/滚动/翻页逻辑
渲染引擎FSCalendarCollectionViewLayout.m自定义 UICollectionView 布局,计算每个日期的格子位置
日期计算器FSCalendarCalculator.mNSDateIndexPath互译的"翻译官"
外观中枢FSCalendarAppearance.m统一管理所有颜色、字体、偏移量
委托代理FSCalendarDelegationProxy.m让日历支持"多委托共存"

💡 记住这张表,后面每个组件都会展开讲。


📂 目录结构导览

整个仓库非常精简,核心源码全部集中在 FSCalendar/ 目录下:

  • FSCalendar/FSCalendar.h —— 对外主接口,定义了FSCalendarDataSourceFSCalendarDelegateFSCalendarDelegateAppearance三大协议
  • FSCalendar/include/ —— 打包成 Framework 时暴露的头文件清单
  • Example-Objc/ —— Objective-C 完整示例工程(DIY 日历、区间选择、范围切换等 Demo)
  • Example-Swift/ —— Swift 示例工程
  • Example-TodayExtension/ —— Today 小组件集成示例
  • FSCalendar.podspec、Package.swift —— CocoaPods 与 SPM 两种集成方式的描述文件

核心源码只有16 对 .h/.m 文件,全部围绕"主控件 + 渲染 + 计算 + 外观 + 委托"五大职责展开,规模小但分工极清晰,非常适合做源码学习。


🏛️ 核心组件一:FSCalendar 主控制器

FSCalendar.h 中声明的FSCalendar继承自UIView,并标记了IB_DESIGNABLE,因此可以直接拖进 Interface Builder 可视化调参(见 FSCalendar (IBExtension) 中的IBInspectable属性)。

打开 FSCalendar.m 的initialize方法(约 146 行起),能看到整个日历的"装配流水线":

  1. 创建外观对象_appearance = [[FSCalendarAppearance alloc] init],并让它反向持有日历;
  2. 创建日期工具NSCalendar(gregorian)+NSDateFormatter,负责所有日期运算;
  3. 搭建视图层级contentView → daysContainer → collectionView,日历的"月视图"本质是嵌在容器里的一个 CollectionView;
  4. 注册三种可复用视图:日期格子FSCalendarCell、空白占位格FSCalendarBlankCell、吸顶头FSCalendarStickyHeader
  5. 挂载两个"助手"transitionCoordinator(过渡动画协调器)和calculator(日期计算器);
  6. 初始化两个委托代理dataSourceProxydelegateProxy(由 FSCalendarDelegationFactory 生产)。

🔍关键发现:主控件还重写了setValue:forUndefinedKey:(FSCalendar.m#L261-L280),把 XIB 中无法识别的属性动态转发给 appearance 或 layout——这就是为什么在 Storyboard 里能直接配置titleDefaultColor这类"日历本体并不存在"的属性,非常巧妙。


⚙️ 核心组件二:CollectionView 与自定义 Layout

日历的"翻页"并不是真的在滚日历,而是每个 Section 代表一个月/一周,横滑切换 Section。这由两个类实现:

  • FSCalendarCollectionView —— 对UICollectionView的轻量封装,增加了一个internalDelegate内部回调通道;
  • FSCalendarCollectionViewLayout —— 继承UICollectionViewLayout,是整个日历渲染的心脏,它决定:
    • 日期格子如何按 7 列排布;
    • 月份头部(Section Header)如何"溶解"(滚动时淡出/淡入);
    • 占位日期(上月/下月补位格)如何定位。

这种"UICollectionView + 自定义 Layout"的组合是 FSCalendar 高性能的关键:格子复用、按需绘制、滚动流畅,全部交给系统成熟机制。


🧮 核心组件三:FSCalendarCalculator 日期计算器

日历控件最棘手的问题是:"2026 年 9 月 18 日"和"第 3 页第 5 格"是两套坐标系,必须在它们之间反复换算。

FSCalendar 把这件事全部封装进了 FSCalendarCalculator,它提供三组"互译"接口:

  • 日期 ↔ IndexPathdateForIndexPath:indexPathForDate:atMonthPosition:scope:,同一个日期在不同"月份位置"(上/本/下月)和不同"视图范围"(月/周)下会算出不同的格子;
  • Section ↔ 页面monthForSection:weekForSection:pageForSection:,把 Section 号还原成具体月份/周;
  • 占位与行数numberOfHeadPlaceholdersForMonth:(月初需要补几个灰格子)、numberOfRowsInMonth:(这个月要占 5 行还是 6 行)。

📐 这就是典型的Calculator 模式:把纯计算逻辑从 UI 代码中剥离,FSCalendar.m中所有日期换算都委托给它,主控制器因此保持"干净"。


🎨 核心组件四:FSCalendarAppearance 外观中枢

想改日历的某个颜色,去 FSCalendarAppearance.h 就能找到对应属性:titleDefaultColor(未选中日字色)、fillSelectionColor(选中填充色)、borderRadiusweekdayFont……几乎所有视觉参数都收敛在这一个对象里。

它还定义了格子状态枚举FSCalendarCellState:

FSCalendarCellStateNormal / Selected / Placeholder / Disabled / Today / Weekend

配合FSCalendarDelegateAppearance协议(定义在 FSCalendar.h#L158 起),FSCalendar 实现了"全局外观 + 按日期精细覆盖"的双层定制体系:

  • 全局:直接改calendar.appearance的属性;
  • 按天:在代理方法中针对特定日期返回不同颜色/圆角/偏移,例如把"工作日"和"周末"画成两种风格。

🔗 核心组件五:Delegation Proxy 多委托机制

UIView原生只允许一个 delegate,但实际开发中,父页面、弹窗管理器、数据层往往都想监听"日期选中"事件。FSCalendar 的解法是Proxy 模式

  • FSCalendarDelegationProxy 维护一个委托队列,FSCalendar内部只和 Proxy 打交道,Proxy 再把调用按优先级分发给所有注册的委托对象
  • FSCalendarDelegationFactory 用工厂方法分别产出 dataSource 版和 delegate 版代理。

结果:你随时可以追加一个 delegate(比如埋点组件),无需覆盖已有委托——这在开源日历库里是相当少见的友好设计。


🎬 辅助组件速览

组件文件作用
过渡协调器FSCalendarTransitionCoordinator.m月/周模式切换(scope 手势)时的缩放动画与布局过渡
月份标题头FSCalendarHeaderView.m显示"9月"等标题,滚动时做"溶解"效果
吸顶头FSCalendarStickyHeader.m非分页模式下,滚动到月份边界时吸附显示月份标题
星期头FSCalendarWeekdayView.m渲染"日一二三四五六",支持zh-CN等多语言 locale
日期格子FSCalendarCell.m单个日期格子视图:数字、副标题、事件圆点、圆角边框
点击分发器FSCalendarDynamicHeader.h点击月份标题直接跳转该月(HeaderTouchDeliver)
工具扩展FSCalendarExtensions.mNSDate/NSCalendar 的便捷分类方法

🧩 设计模式总结

FSCalendar 的代码量不大,但模式运用得相当完整,值得逐个对照源码学习:

  • 组合优于继承:FSCalendar 不继承 UIScrollView,而是组合 CollectionView,行为更可控;
  • DataSource / Delegate:数据(事件数、副标题)与行为(选中回调)分离,与 UICollectionView 一脉相承,降低学习成本;
  • Calculator 模式:日期与坐标系的换算独立成类;
  • Proxy 模式:多委托共存;
  • Coordinator 模式FSCalendarTransitionCoordinator把"月↔周切换"动画从主流程中剥离;
  • 单元格复用:基于 UICollectionView 的 Cell 复用机制保证流畅滚动。

📚源码阅读路线建议:先读 FSCalendar.m 的initializelayoutSubviews→ 再看 FSCalendarCalculator.h 的接口设计 → 最后对照 Example-Objc/DIYExampleViewController.m 等示例验证理解。


📝 小结与下篇预告

FSCalendar 的架构可以浓缩为一句话:主控制器负责编排,Layout 负责渲染,Calculator 负责换算,Appearance 负责颜值,Proxy 负责协作。五个组件各司其职,才让一个"日期格子网格"变得既可定制又高效。

⏭️下篇预告(源码解析二):我们将深入日期选中与范围选择的状态流转、月/周 scope 切换动画的完整实现,以及 Today Extension 小组件的集成细节。

更多使用技巧可参考 MOREUSAGE.md 与 README.md。

【免费下载链接】FSCalendarA fully customizable iOS calendar library, compatible with Objective-C and Swift项目地址: https://gitcode.com/gh_mirrors/fs/FSCalendar

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询