FSCalendar基础使用与界面构建指南
本文详细介绍了FSCalendar日历组件的两种构建方式:通过Interface Builder可视化构建和通过代码方式创建实例。涵盖了Storyboard集成步骤、界面属性配置、数据源与代理协议实现方法,以及自动布局约束的动态调整技巧,帮助开发者快速掌握FSCalendar的核心功能和使用方法。
Interface Builder可视化构建日历界面
FSCalendar提供了强大的Interface Builder支持,让开发者能够通过可视化方式快速构建日历界面,无需编写大量布局代码。这种可视化构建方式不仅提高了开发效率,还能实时预览界面效果。
Storyboard集成步骤
通过Interface Builder集成FSCalendar只需几个简单步骤:
- 添加UIView控件:在Storyboard中拖拽一个UIView到ViewController的场景中
- 设置自定义类:在Identity Inspector中将Custom Class设置为
FSCalendar - 连接代理和数据源:将dataSource和delegate连接到ViewController
- 配置约束:设置适当的Auto Layout约束
flowchart TD
A[拖拽UIView到Storyboard] --> B[设置Custom Class为FSCalendar]
B --> C[连接dataSource和delegate]
C --> D[配置Auto Layout约束]
D --> E[实现代理和数据源方法]
E --> F[运行应用查看效果]
界面属性配置
FSCalendar在Interface Builder中提供了丰富的可配置属性,这些属性都支持IBInspectable,可以直接在Attributes Inspector中设置:
| 属性类别 | 配置项 | 说明 | 示例值 |
|---|---|---|---|
| 基本设置 | scrollDirection | 滚动方向 | Horizontal / Vertical |
| scope | 显示范围 | Month / Week | |
| firstWeekday | 每周第一天 | 1(周日) / 2(周一) | |
| 外观设置 | headerDateFormat | 头部日期格式 | "MMMM yyyy" |
| borderRadius | 日期单元格圆角 | 1.0 | |
| 颜色设置 | headerTitleColor | 头部标题颜色 | UIColor.red |
| weekdayTextColor | 周几文本颜色 | UIColor.darkGray | |
| todayColor | 今天背景色 | UIColor.orange |
约束配置指南
在Interface Builder中配置FSCalendar的约束时,需要特别注意高度约束的处理。由于FSCalendar在不同模式下(月/周)会有不同的高度,需要实现boundingRectWillChange代理方法来动态调整高度约束。
// Auto Layout约束配置示例
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated
{
self.calendarHeightConstraint.constant = CGRectGetHeight(bounds);
[self.view layoutIfNeeded];
}
实时预览功能
FSCalendar支持IBDesignable,这意味着在Interface Builder中可以直接看到日历的实时渲染效果,无需编译运行。这个功能极大地提高了开发效率,可以即时调整外观和布局。
sequenceDiagram
participant IB as Interface Builder
participant FSC as FSCalendar(IBDesignable)
participant VC as ViewController
IB->>FSC: 渲染请求
FSC->>FSC: 初始化日历视图
FSC->>FSC: 应用IBInspectable属性
FSC->>VC: 请求数据源信息(可选)
FSC->>IB: 返回渲染后的视图
IB->>Developer: 显示实时预览
数据源和代理连接
在Storyboard中正确连接dataSource和delegate是至关重要的。以下是完整的连接流程:
- 在Storyboard中右键点击FSCalendar视图
- 将dataSource拖拽到ViewController
- 将delegate拖拽到ViewController
- 在ViewController中实现必要的协议方法
// 必须实现的数据源方法
- (NSDate *)minimumDateForCalendar:(FSCalendar *)calendar;
- (NSDate *)maximumDateForCalendar:(FSCalendar *)calendar;
// 可选的装饰方法
- (NSString *)calendar:(FSCalendar *)calendar titleForDate:(NSDate *)date;
- (NSString *)calendar:(FSCalendar *)calendar subtitleForDate:(NSDate *)date;
- (NSInteger)calendar:(FSCalendar *)calendar numberOfEventsForDate:(NSDate *)date;
主题切换实现
通过Interface Builder构建的日历界面可以轻松实现主题切换功能。以下是一个主题配置的示例:
- (void)configureTheme:(NSInteger)themeIndex {
switch (themeIndex) {
case 0: // 默认主题
self.calendar.appearance.weekdayTextColor = FSCalendarStandardTitleTextColor;
self.calendar.appearance.headerTitleColor = FSCalendarStandardTitleTextColor;
self.calendar.appearance.selectionColor = FSCalendarStandardSelectionColor;
self.calendar.appearance.headerDateFormat = @"MMMM yyyy";
break;
case 1: // 自定义主题
self.calendar.appearance.weekdayTextColor = [UIColor redColor];
self.calendar.appearance.headerTitleColor = [UIColor darkGrayColor];
self.calendar.appearance.selectionColor = [UIColor blueColor];
self.calendar.appearance.headerDateFormat = @"yyyy-MM";
break;
}
[self.calendar reloadAppearance];
}
常见问题解决
在使用Interface Builder集成FSCalendar时可能会遇到的一些常见问题及解决方案:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 日历不显示 | dataSource未连接 | 检查Storyboard中的连接 |
| 高度不正确 | 未实现boundingRectWillChange | 实现高度调整代理方法 |
| 样式不生效 | 代码中重复设置 | 避免在viewDidLoad中覆盖IB设置 |
| 性能问题 | 数据源方法复杂 | 优化数据查询逻辑 |
通过Interface Builder可视化构建FSCalendar界面,开发者可以快速创建出功能丰富、外观精美的日历组件,大大提升开发效率和用户体验。
代码方式创建FSCalendar实例的完整流程
在iOS开发中,通过代码方式创建FSCalendar实例提供了最大的灵活性和控制力。这种方式特别适合需要动态调整界面布局、实现复杂交互逻辑或者集成到自定义视图层级中的场景。下面将详细介绍完整的创建流程。
初始化FSCalendar实例
首先需要在视图控制器的适当生命周期方法中创建FSCalendar实例。推荐在loadView或viewDidLoad方法中进行初始化:
// 在视图控制器的接口部分声明属性
@property (weak, nonatomic) FSCalendar *calendar;
// 在实现部分创建实例
- (void)viewDidLoad {
[super viewDidLoad];
// 计算合适的日历高度(根据设备类型调整)
CGFloat height = [[UIDevice currentDevice].model hasPrefix:@"iPad"] ? 450 : 300;
// 创建FSCalendar实例并设置frame
FSCalendar *calendar = [[FSCalendar alloc] initWithFrame:CGRectMake(0, 64, self.view.bounds.size.width, height)];
// 设置数据源和代理
calendar.dataSource = self;
calendar.delegate = self;
// 设置背景颜色
calendar.backgroundColor = [UIColor whiteColor];
// 添加到视图层级
[self.view addSubview:calendar];
self.calendar = calendar;
}
配置基本外观属性
FSCalendar提供了丰富的外观配置选项,可以通过appearance属性进行个性化设置:
// 配置外观属性
calendar.appearance.headerMinimumDissolvedAlpha = 0; // 头部标题不透明
calendar.appearance.caseOptions = FSCalendarCaseOptionsHeaderUsesUpperCase; // 头部使用大写
calendar.appearance.weekdayTextColor = [UIColor darkGrayColor]; // 星期文字颜色
calendar.appearance.headerTitleColor = [UIColor blackColor]; // 头部标题颜色
calendar.appearance.eventDefaultColor = [UIColor greenColor]; // 事件点默认颜色
calendar.appearance.selectionColor = [UIColor blueColor]; // 选中颜色
calendar.appearance.todayColor = [UIColor redColor]; // 今天背景色
calendar.appearance.todaySelectionColor = [UIColor purpleColor]; // 今天选中颜色
实现必要的数据源和代理方法
为了FSCalendar正常工作,需要实现基本的数据源和代理方法:
#pragma mark - FSCalendarDataSource
// 设置最小日期
- (NSDate *)minimumDateForCalendar:(FSCalendar *)calendar {
return [self.gregorian dateWithEra:1 year:2020 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0];
}
// 设置最大日期
- (NSDate *)maximumDateForCalendar:(FSCalendar *)calendar {
return [NSDate date];
}
// 为特定日期提供事件数量
- (NSInteger)calendar:(FSCalendar *)calendar numberOfEventsForDate:(NSDate *)date {
// 根据业务逻辑返回事件数量
return 0;
}
#pragma mark - FSCalendarDelegate
// 处理日期选择事件
- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition {
NSLog(@"选中日期: %@", date);
}
// 处理边界变化事件(重要!)
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated {
// 更新日历的frame以适应新的边界
calendar.frame = (CGRect){calendar.frame.origin, bounds.size};
// 如果有其他需要调整的UI元素,在这里进行更新
[self adjustOtherUIElements];
}
完整的初始化流程时序图
以下是代码创建FSCalendar实例的完整时序流程:
sequenceDiagram
participant VC as ViewController
participant Calendar as FSCalendar
participant DataSource as DataSource
participant Delegate as Delegate
VC->>VC: viewDidLoad/lifecycle method
VC->>Calendar: alloc initWithFrame:
VC->>Calendar: setDataSource:self
VC->>Calendar: setDelegate:self
VC->>Calendar: set appearance properties
VC->>VC: addSubview:calendar
VC->>VC: store weak reference
Calendar->>DataSource: minimumDateForCalendar:
Calendar->>DataSource: maximumDateForCalendar:
Calendar->>DataSource: numberOfEventsForDate:
Note over Calendar: 渲染日历界面
Calendar->>Delegate: boundingRectWillChange:animated:
Note over VC: 用户交互开始
User->>Calendar: tap on date
Calendar->>Delegate: didSelectDate:atMonthPosition:
关键配置参数说明
下表列出了代码创建时需要关注的关键配置参数:
| 参数类别 | 配置方法 | 说明 | 示例值 |
|---|---|---|---|
| 框架设置 | initWithFrame: |
设置日历的位置和大小 | CGRectMake(0, 64, 320, 300) |
| 数据源 | dataSource |
设置数据源对象 | self |
| 代理 | delegate |
设置代理对象 | self |
| 外观 | appearance.* |
各种外观属性配置 | 见上文示例 |
| 本地化 | locale |
设置地区本地化 | [NSLocale localeWithLocaleIdentifier:@"zh-CN"] |
| 滚动方向 | scrollDirection |
设置滚动方向 | FSCalendarScrollDirectionVertical |
| 范围模式 | scope |
设置月/周视图模式 | FSCalendarScopeMonth |
注意事项和最佳实践
- 内存管理:使用weak属性引用calendar实例,避免循环引用
- 边界调整:必须实现
boundingRectWillChange:animated:方法来处理日历大小变化 - 性能优化:对于复杂的数据源方法,考虑使用缓存机制提高性能
- 设备适配:根据设备类型调整日历的初始高度和布局
- 错误处理:确保数据源方法返回有效的日期范围,避免显示异常
通过代码方式创建FSCalendar实例虽然需要编写更多的代码,但提供了最大的灵活性和控制能力,特别适合需要深度定制和复杂交互的场景。
数据源和代理协议的基本实现方法
FSCalendar作为iOS平台上一个功能强大的日历控件,其核心功能通过数据源(DataSource)和代理(Delegate)协议来实现。这两个协议分别负责数据的提供和用户交互的处理,是FSCalendar与应用程序进行通信的关键桥梁。
数据源协议(FSCalendarDataSource)
数据源协议主要负责向日历提供显示所需的数据内容,包括日期标题、副标题、事件数量以及自定义单元格等。
基本数据提供方法
1. 日期标题定制
- (NSString *)calendar:(FSCalendar *)calendar titleForDate:(NSDate *)date {
// 返回特定日期的自定义标题
NSCalendar *gregorian = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSInteger day = [gregorian component:NSCalendarUnitDay fromDate:date];
if (day % 7 == 0) {
return [NSString stringWithFormat:@"%ld\n休息日", (long)day];
}
return [NSString stringWithFormat:@"%ld", (long)day];
}
2. 日期副标题添加
- (NSString *)calendar:(FSCalendar *)calendar subtitleForDate:(NSDate *)date {
// 为特定日期添加副标题信息
NSCalendar *gregorian = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSInteger day = [gregorian component:NSCalendarUnitDay fromDate:date];
if (day == 15) {
return @"发薪日";
} else if (day == 1) {
return @"月初";
}
return nil;
}
3. 事件数量指示
- (NSInteger)calendar:(FSCalendar *)calendar numberOfEventsForDate:(NSDate *)date {
// 返回日期对应的事件数量,用于显示事件点
NSCalendar *gregorian = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSInteger day = [gregorian component:NSCalendarUnitDay fromDate:date];
// 示例:每月5号、15号、25号有事件
if (day % 10 == 5) {
return 1;
}
return 0;
}
日期范围控制
- (NSDate *)minimumDateForCalendar:(FSCalendar *)calendar {
// 设置日历显示的最小日期
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd";
return [formatter dateFromString:@"2024-01-01"];
}
- (NSDate *)maximumDateForCalendar:(FSCalendar *)calendar {
// 设置日历显示的最大日期
NSDate *currentDate = [NSDate date];
NSCalendar *gregorian = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *components = [[NSDateComponents alloc] init];
components.year = 1;
return [gregorian dateByAddingComponents:components toDate:currentDate options:0];
}
代理协议(FSCalendarDelegate)
代理协议处理用户的交互行为,包括日期选择、范围变化、页面切换等事件。
选择事件处理
// 日期选择前验证
- (BOOL)calendar:(FSCalendar *)calendar shouldSelectDate:(NSDate *)date
atMonthPosition:(FSCalendarMonthPosition)monthPosition {
// 禁止选择周末
NSCalendar *gregorian = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSInteger weekday = [gregorian component:NSCalendarUnitWeekday fromDate:date];
return (weekday != 1 && weekday != 7); // 1=周日, 7=周六
}
// 日期选择完成
- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date
atMonthPosition:(FSCalendarMonthPosition)monthPosition {
NSLog(@"选中日期: %@", date);
// 更新界面或执行其他操作
}
// 日期取消选择
- (void)calendar:(FSCalendar *)calendar didDeselectDate:(NSDate *)date
atMonthPosition:(FSCalendarMonthPosition)monthPosition {
NSLog(@"取消选择日期: %@", date);
}
布局变化处理
// 日历边界变化处理(重要:必须实现)
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds
animated:(BOOL)animated {
// 更新日历高度约束
self.calendarHeightConstraint.constant = bounds.size.height;
[self.view layoutIfNeeded];
}
页面切换事件
// 当前页面发生变化
- (void)calendarCurrentPageDidChange:(FSCalendar *)calendar {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy年MM月";
NSString *currentPage = [formatter stringFromDate:calendar.currentPage];
NSLog(@"切换到: %@", currentPage);
self.title = currentPage; // 更新导航栏标题
}
协议方法的组合使用模式
在实际开发中,数据源和代理方法通常需要组合使用来实现复杂的功能需求。以下是一个典型的使用模式:
flowchart TD
A[用户交互] --> B[代理方法验证]
B --> C{是否允许操作?}
C -->|是| D[执行操作]
C -->|否| E[阻止操作]
D --> F[更新数据源]
F --> G[刷新界面显示]
实用示例:完整的日历控制器实现
@interface CalendarViewController () <FSCalendarDataSource, FSCalendarDelegate>
@property (weak, nonatomic) FSCalendar *calendar;
@property (strong, nonatomic) NSCalendar *gregorian;
@property (strong, nonatomic) NSMutableSet *selectedDates;
@end
@implementation CalendarViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.gregorian = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
self.selectedDates = [NSMutableSet set];
[self setupCalendar];
}
- (void)setupCalendar {
FSCalendar *calendar = [[FSCalendar alloc] initWithFrame:CGRectMake(0, 88, self.view.bounds.size.width, 300)];
calendar.dataSource = self;
calendar.delegate = self;
calendar.allowsMultipleSelection = YES;
[self.view addSubview:calendar];
self.calendar = calendar;
}
#pragma mark - FSCalendarDataSource
- (NSInteger)calendar:(FSCalendar *)calendar numberOfEventsForDate:(NSDate *)date {
// 业务逻辑:每月1号和15号有重要事件
NSInteger day = [self.gregorian component:NSCalendarUnitDay fromDate:date];
return (day == 1 || day == 15) ? 1 : 0;
}
- (NSString *)calendar:(FSCalendar *)calendar subtitleForDate:(NSDate *)date {
// 业务逻辑:为特定日期添加备注
NSInteger day = [self.gregorian component:NSCalendarUnitDay fromDate:date];
if (day == 1) return @"月初";
if (day == 15) return @"月中";
if (day == [self.gregorian component:NSCalendarUnitDay fromDate:[NSDate date]]) return @"今天";
return nil;
}
#pragma mark - FSCalendarDelegate
- (BOOL)calendar:(FSCalendar *)calendar shouldSelectDate:(NSDate *)date {
// 业务逻辑:禁止选择过去日期
return [date compare:[NSDate date]] != NSOrderedAscending;
}
- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date {
[self.selectedDates addObject:date];
NSLog(@"已选择 %ld 个日期", (long)self.selectedDates.count);
}
- (void)calendar:(FSCalendar *)calendar didDeselectDate:(NSDate *)date {
[self.selectedDates removeObject:date];
NSLog(@"剩余 %ld 个选择", (long)self.selectedDates.count);
}
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated {
// 动态调整日历高度
calendar.frame = CGRectMake(0, 88, self.view.bounds.size.width, bounds.size.height);
}
@end
性能优化建议
- 数据缓存:对于复杂的数据计算,建议使用缓存机制避免重复计算
- 懒加载:在需要时才创建和计算数据
- 批量操作:对于大量日期数据的处理,使用批量操作提高效率
- 内存管理:及时释放不再使用的资源,避免内存泄漏
通过合理实现数据源和代理协议的方法,可以充分发挥FSCalendar的强大功能,创建出既美观又实用的日历界面。
自动布局约束的动态调整技巧
FSCalendar作为一个高度可定制的iOS日历组件,其界面布局的动态调整是开发过程中经常遇到的需求。特别是在处理日历范围切换(Scope Change)、月份切换以及隐藏占位符等场景时,自动布局约束的动态调整显得尤为重要。本文将深入探讨FSCalendar中自动布局约束的动态调整技巧,帮助开发者构建更加灵活和响应式的界面。
理解boundingRectWillChange委托方法
FSCalendar通过boundingRectWillChange委托方法来通知界面尺寸的变化。这个方法在以下情况下会被调用:
- 日历范围从月视图切换到周视图或反之
- 月份切换时隐藏占位符导致的高度变化
- 设备旋转时的布局调整
- (void)calendar:(FSCalendar *)calendar
boundingRectWillChange:(CGRect)bounds
animated:(BOOL)animated
{
// 在这里处理布局约束的更新
}
自动布局约束的动态更新策略
1. 使用NSLayoutConstraint更新高度约束
最常见的场景是更新日历的高度约束。通过维护一个对高度约束的引用,可以在boundingRectWillChange方法中动态调整:
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *calendarHeightConstraint;
- (void)calendar:(FSCalendar *)calendar
boundingRectWillChange:(CGRect)bounds
animated:(BOOL)animated
{
self.calendarHeightConstraint.constant = CGRectGetHeight(bounds);
[self.view layoutIfNeeded];
}
2. 多约束协同更新
在实际项目中,日历的高度变化往往需要其他界面元素同步调整。以下是一个复杂的多约束更新示例:
flowchart TD
A[boundingRectWillChange被调用] --> B[获取新的bounds尺寸]
B --> C[更新日历高度约束]
C --> D[计算表格视图的偏移量]
D --> E[调整表格视图约束]
E --> F[更新其他相关界面元素]
F --> G[执行布局动画]
G --> H[布局更新完成]
- (void)calendar:(FSCalendar *)calendar
boundingRectWillChange:(CGRect)bounds
animated:(BOOL)animated
{
CGFloat newHeight = CGRectGetHeight(bounds);
CGFloat heightDelta = newHeight - self.calendarHeightConstraint.constant;
// 更新日历高度约束
self.calendarHeightConstraint.constant = newHeight;
// 同步调整表格视图的顶部约束
self.tableViewTopConstraint.constant += heightDelta;
// 如果有其他相关视图,也需要相应调整
self.headerViewHeightConstraint.constant = newHeight > 300 ? 60 : 44;
[UIView animateWithDuration:animated ? 0.3 : 0 animations:^{
[self.view layoutIfNeeded];
}];
}
3. 使用Masonry进行约束更新
对于使用Masonry的项目,约束更新可以采用以下方式:
- (void)calendar:(FSCalendar *)calendar
boundingRectWillChange:(CGRect)bounds
animated:(BOOL)animated
{
[self.calendar mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@(CGRectGetHeight(bounds)));
}];
[self.tableView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.calendar.mas_bottom).offset(10);
}];
[self.view layoutIfNeeded];
}
4. 使用SnapKit(Swift项目)
Swift项目中使用SnapKit的约束更新示例:
func calendar(_ calendar: FSCalendar,
boundingRectWillChange bounds: CGRect,
animated: Bool) {
calendar.snp.updateConstraints { make in
make.height.equalTo(bounds.height)
}
tableView.snp.updateConstraints { make in
make.top.equalTo(calendar.snp.bottom).offset(10)
}
UIView.animate(withDuration: animated ? 0.3 : 0) {
self.view.layoutIfNeeded()
}
}
高级动态布局技巧
1. 响应式界面设计
通过结合boundingRectWillChange和其他界面事件,可以创建真正响应式的布局:
- (void)calendar:(FSCalendar *)calendar
boundingRectWillChange:(CGRect)bounds
animated:(BOOL)animated
{
CGFloat newHeight = CGRectGetHeight(bounds);
// 根据高度决定界面元素的显示状态
BOOL shouldShowAdditionalControls = newHeight > 250;
[UIView animateWithDuration:animated ? 0.3 : 0 animations:^{
self.calendarHeightConstraint.constant = newHeight;
self.additionalControlsView.alpha = shouldShowAdditionalControls ? 1.0 : 0.0;
[self.view layoutIfNeeded];
}];
}
2. 性能优化的约束更新
对于复杂的界面,约束更新可能会影响性能。以下是一些优化技巧:
- (void)calendar:(FSCalendar *)calendar
boundingRectWillChange:(CGRect)bounds
animated:(BOOL)animated
{
// 只在高度变化超过阈值时更新约束
CGFloat newHeight = CGRectGetHeight(bounds);
if (fabs(newHeight - self.calendarHeightConstraint.constant) < 1.0) {
return;
}
self.calendarHeightConstraint.constant = newHeight;
// 批量更新约束,减少布局传递次数
[self.view setNeedsUpdateConstraints];
if (animated) {
[UIView animateWithDuration:0.3
animations:^{
[self.view layoutIfNeeded];
}];
} else {
[self.view layoutIfNeeded];
}
}
3. 结合其他界面事件的综合处理
sequenceDiagram
participant User
participant Calendar
participant ViewController
participant LayoutSystem
User->>Calendar: 切换范围/旋转设备
Calendar->>ViewController: boundingRectWillChange
ViewController->>ViewController: 计算新约束值
ViewController->>LayoutSystem: 更新约束
LayoutSystem->>LayoutSystem: 计算新布局
LayoutSystem->>ViewController: 布局完成
ViewController->>Calendar: 回调完成
实际应用场景示例
场景1:日历与表格视图的协同滚动
- (void)calendar:(FSCalendar *)calendar
boundingRectWillChange:(CGRect)bounds
animated:(BOOL)animated
{
CGFloat oldHeight = self.calendarHeightConstraint.constant;
CGFloat newHeight = CGRectGetHeight(bounds);
CGFloat delta = newHeight - oldHeight;
// 更新日历高度
self.calendarHeightConstraint.constant = newHeight;
// 调整表格视图的内容偏移,保持视觉连续性
CGPoint currentOffset = self.tableView.contentOffset;
self.tableView.contentOffset = CGPointMake(currentOffset.x, currentOffset.y + delta);
[UIView animateWithDuration:animated ? 0.3 : 0 animations:^{
[self.view layoutIfNeeded];
}];
}
场景2:多设备适配的动态布局
- (void)calendar:(FSCalendar *)calendar
boundingRectWillChange:(CGRect)bounds
animated:(BOOL)animated
{
CGFloat newHeight = CGRectGetHeight(bounds);
self.calendarHeightConstraint.constant = newHeight;
// 根据设备类型和方向调整其他约束
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
self.sidebarWidthConstraint.constant = newHeight > 400 ? 200 : 150;
} else {
self.sidebarWidthConstraint.constant = newHeight > 300 ? 120 : 80;
}
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:animated ? 0.3 : 0 animations:^{
[self.view layoutIfNeeded];
}];
}
约束更新最佳实践表格
| 场景 | 推荐方法 | 注意事项 |
|---|---|---|
| 简单高度调整 | 直接更新高度约束 | 确保约束引用正确 |
| 复杂界面更新 | 批量更新约束 | 使用setNeedsUpdateConstraints |
| 动画效果 | UIView animateWithDuration | 同步其他界面元素的动画 |
| 性能敏感场景 | 阈值检查 | 避免不必要的布局传递 |
| 多设备适配 | 条件约束更新 | 根据设备特征调整约束值 |
通过掌握这些自动布局约束的动态调整技巧,开发者可以创建出更加灵活、响应迅速的FSCalendar界面,为用户提供更好的交互体验。关键在于理解boundingRectWillChange的工作原理,并根据具体需求选择合适的约束更新策略。
FSCalendar作为iOS平台上功能强大的日历组件,提供了灵活的可视化构建和代码创建两种方式。通过合理实现数据源和代理协议,结合自动布局约束的动态调整技巧,开发者可以创建出既美观又实用的日历界面。掌握这些核心功能和使用方法,能够大大提升开发效率和用户体验,满足各种复杂的日历需求场景。
Kimi-K2.5Kimi K2.5 是一款开源的原生多模态智能体模型,它在 Kimi-K2-Base 的基础上,通过对约 15 万亿混合视觉和文本 tokens 进行持续预训练构建而成。该模型将视觉与语言理解、高级智能体能力、即时模式与思考模式,以及对话式与智能体范式无缝融合。Python00- QQwen3-Coder-Next2026年2月4日,正式发布的Qwen3-Coder-Next,一款专为编码智能体和本地开发场景设计的开源语言模型。Python00
xw-cli实现国产算力大模型零门槛部署,一键跑通 Qwen、GLM-4.7、Minimax-2.1、DeepSeek-OCR 等模型Go06
PaddleOCR-VL-1.5PaddleOCR-VL-1.5 是 PaddleOCR-VL 的新一代进阶模型,在 OmniDocBench v1.5 上实现了 94.5% 的全新 state-of-the-art 准确率。 为了严格评估模型在真实物理畸变下的鲁棒性——包括扫描伪影、倾斜、扭曲、屏幕拍摄和光照变化——我们提出了 Real5-OmniDocBench 基准测试集。实验结果表明,该增强模型在新构建的基准测试集上达到了 SOTA 性能。此外,我们通过整合印章识别和文本检测识别(text spotting)任务扩展了模型的能力,同时保持 0.9B 的超紧凑 VLM 规模,具备高效率特性。Python00
KuiklyUI基于KMP技术的高性能、全平台开发框架,具备统一代码库、极致易用性和动态灵活性。 Provide a high-performance, full-platform development framework with unified codebase, ultimate ease of use, and dynamic flexibility. 注意:本仓库为Github仓库镜像,PR或Issue请移步至Github发起,感谢支持!Kotlin08
VLOOKVLOOK™ 是优雅好用的 Typora/Markdown 主题包和增强插件。 VLOOK™ is an elegant and practical THEME PACKAGE × ENHANCEMENT PLUGIN for Typora/Markdown.Less00