项目需要,前一阵子重构了下iPad工程,添加了一个滚动无缝日历。
当时没有头绪,网上找了一个源码改吧改吧就上线了(参考链接),这个功能很多而且流畅性也特别好,推荐不会写的可以参考下。
这几天,活不太忙就把日历控件裁剪了下,做个最简单的滚动无缝日历。效果如下图:
日历可以左右滚动,点击某个日期后会变色,并且有回调。橘色的是标记日期,蓝色的是选择日期,蓝边的是当前日期,可以根据需要自行更改。
这个日历控件有两个比较复杂的地方:
- UICollectionView默认情况下,横滚cell竖排竖滚cell横排,所以我们先要修改下cell的位置,自定义FlowLayout继承于UICollectionViewFlowLayout,重写它的prepareLayout方法。
#import \"EXCalendarCollectionViewFlowLayout.h\" @interface EXCalendarCollectionViewFlowLayout () @property (nonatomic, strong) NSMutableArray *allAttributes; @end @implementation EXCalendarCollectionViewFlowLayout - (void)prepareLayout { [super prepareLayout]; self.allAttributes = [NSMutableArray array]; NSInteger sections = [self.collectionView numberOfSections]; for (int i = 0; i < sections; i++) { // setup one section attributes. NSMutableArray *tmpArray = [NSMutableArray array]; NSInteger count = [self.collectionView numberOfItemsInSection:i]; for (NSInteger j = 0; j < count; j++) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:j inSection:i]; UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; [tmpArray addObject:attributes]; } [self.allAttributes addObject:tmpArray]; } } - (CGSize)collectionViewContentSize { return [super collectionViewContentSize]; } - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { NSInteger item = indexPath.item; NSInteger x; NSInteger y; // 根据item的序号计算出item的行列位置 [self targetPositionWithItem:item resultX:&x resultY:&y]; // 根据已得出的item的行列位置,将item放入indexPath中对应的位置。 NSInteger item2 = [self orignItemAtX:x y:y]; NSIndexPath *theNewIndexPath = [NSIndexPath indexPathForItem:item2 inSection:indexPath.section]; UICollectionViewLayoutAttributes *theNewAttr = [super layoutAttributesForItemAtIndexPath:theNewIndexPath]; theNewAttr.indexPath = indexPath; return theNewAttr; } - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray *attributes = [super layoutAttributesForElementsInRect:rect]; NSMutableArray *tmp = [NSMutableArray array]; for (UICollectionViewLayoutAttributes *attr in attributes) { for (NSMutableArray *attributes in self.allAttributes) { for (UICollectionViewLayoutAttributes *attr2 in attributes) { if (attr.indexPath.item == attr2.indexPath.item) { [tmp addObject:attr2]; break; } } } } return tmp; } // 根据item计算目标item的位置。 - (void)targetPositionWithItem:(NSInteger)item resultX:(NSInteger *)x resultY:(NSInteger *)y { // NSInteger page = item / (self.itemCountPerRow * self.rowCountPerPage); NSInteger theX = item % self.itemCountPerRow; NSInteger theY = item / self.itemCountPerRow; if (x != NULL) { *x = theX; } if (y != NULL) { *y = theY; } } - (NSInteger)orignItemAtX:(NSInteger)x y:(NSInteger)y { NSInteger item = x * self.rowCountPerPage + y; return item; } @end
View Code
- 当你在当前月份点击了一个日期,滑到其他月份,然后要对刚才选择的月份的效果进行更改时,比较麻烦。刚开始我在didSelectItemAtIndexPath委托方法中用cellForItemAtIndexPath进行获取时,不可见的cell获取不到返回的是空,然后在如何获取不可见的cell问题上纠结了两天,最终换了个解决方案,在cellForItemAtIndexPath中进行了判断,解决了这个问题,当然点击后直接有响应跳转的话,刚才这个功能就很鸡肋了。具体看代码吧。
源码地址:https://github.com/zhanghua0926/EXCalendar