From 6784270167b0576c3042ead42821988ce8dfacb4 Mon Sep 17 00:00:00 2001 From: WenchaoIOS Date: Sat, 7 Nov 2015 09:08:56 +0800 Subject: [PATCH 1/6] Update Example 1. Update Example 2. Single instance NSDateFormatter 3. Fix performance issue --- Example/LoadViewExampleViewController.h | 2 + Example/LoadViewExampleViewController.m | 14 ++- Example/StoryboardExampleViewController.h | 3 + Example/StoryboardExampleViewController.m | 42 +++++--- FSCalendar/FSCalendar.m | 124 ++++++++-------------- FSCalendar/NSDate+FSExtension.h | 6 ++ FSCalendar/NSDate+FSExtension.m | 19 +++- FSCalendar/NSString+FSExtension.m | 3 +- 8 files changed, 112 insertions(+), 101 deletions(-) diff --git a/Example/LoadViewExampleViewController.h b/Example/LoadViewExampleViewController.h index 83caadd..6ce292a 100644 --- a/Example/LoadViewExampleViewController.h +++ b/Example/LoadViewExampleViewController.h @@ -13,4 +13,6 @@ @property (weak, nonatomic) FSCalendar *calendar; +@property (strong, nonatomic) NSDictionary *images; + @end diff --git a/Example/LoadViewExampleViewController.m b/Example/LoadViewExampleViewController.m index 8e62f7d..896d4e4 100644 --- a/Example/LoadViewExampleViewController.m +++ b/Example/LoadViewExampleViewController.m @@ -22,6 +22,10 @@ self = [super init]; if (self) { self.title = @"FSCalendar"; + self.images = @{@"2015/02/01":[UIImage imageNamed:@"icon_cat"], + @"2015/02/05":[UIImage imageNamed:@"icon_footprint"], + @"2015/02/20":[UIImage imageNamed:@"icon_cat"], + @"2015/03/07":[UIImage imageNamed:@"icon_footprint"]}; } return self; } @@ -40,7 +44,7 @@ calendar.scrollDirection = FSCalendarScrollDirectionVertical; // calendar.scrollEnabled = NO; // calendar.scope = FSCalendarScopeWeek; - [calendar selectDate:[NSDate fs_dateWithYear:2015 month:2 day:1]]; + [calendar selectDate:[NSDate fs_dateWithYear:2015 month:2 day:6]]; calendar.backgroundColor = [UIColor whiteColor]; [view addSubview:calendar]; self.calendar = calendar; @@ -89,13 +93,7 @@ - (UIImage *)calendar:(FSCalendar *)calendar imageForDate:(NSDate *)date { - if (date.fs_day == 5) { - return [UIImage imageNamed:@"icon_footprint"]; - } - if (date.fs_day == 10 || date.fs_day == 15) { - return [UIImage imageNamed:@"icon_cat"]; - } - return nil; + return self.images[[date fs_stringWithFormat:@"yyyy/MM/dd"]]; } @end diff --git a/Example/StoryboardExampleViewController.h b/Example/StoryboardExampleViewController.h index 0c11a95..328e4e9 100644 --- a/Example/StoryboardExampleViewController.h +++ b/Example/StoryboardExampleViewController.h @@ -19,6 +19,9 @@ @property (strong, nonatomic) NSDate *selectedDate; @property (assign, nonatomic) NSUInteger firstWeekday; +@property (strong, nonatomic) NSArray *datesShouldNotBeSelected; +@property (strong, nonatomic) NSArray *datesWithEvent; + @property (weak, nonatomic) IBOutlet NSLayoutConstraint *calendarHeightConstraint; @end diff --git a/Example/StoryboardExampleViewController.m b/Example/StoryboardExampleViewController.m index 6ddedb1..6cb083c 100644 --- a/Example/StoryboardExampleViewController.m +++ b/Example/StoryboardExampleViewController.m @@ -30,21 +30,36 @@ self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleBordered target:nil action:nil]; _currentCalendar = [NSCalendar currentCalendar]; + _scrollDirection = _calendar.scrollDirection; + _calendar.appearance.caseOptions = FSCalendarCaseOptionsHeaderUsesUpperCase|FSCalendarCaseOptionsWeekdayUsesUpperCase; + + [_calendar selectDate:[NSDate fs_dateWithYear:2015 month:10 day:5]]; // _firstWeekday = _calendar.firstWeekday; // _calendar.firstWeekday = 2; // Monday // _calendar.flow = FSCalendarFlowVertical; // _calendar.selectedDate = [NSDate fs_dateWithYear:2015 month:2 day:1]; - _scrollDirection = _calendar.scrollDirection; // _calendar.appearance.useVeryShortWeekdaySymbols = YES; // _calendar.scope = FSCalendarScopeWeek; // _calendar.allowsMultipleSelection = YES; - _calendar.appearance.caseOptions = FSCalendarCaseOptionsHeaderUsesUpperCase|FSCalendarCaseOptionsWeekdayUsesUpperCase; - [_calendar selectDate:[NSDate date]]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [_calendar deselectDate:[NSDate date]]; +// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ +// [_calendar deselectDate:[NSDate date]]; // _calendar.appearance.caseOptions = FSCalendarCaseOptionsHeaderUsesDefaultCase|FSCalendarCaseOptionsWeekdayUsesSingleUpperCase; - }); +// }); + + + _datesShouldNotBeSelected = @[@"2015/08/07", + @"2015/09/07", + @"2015/10/07", + @"2015/11/07", + @"2015/12/07", + @"2016/01/07", + @"2016/02/07"]; + + _datesWithEvent = @[@"2015-10-03", + @"2015-10-07", + @"2015-10-15", + @"2015-10-25"]; #if 0 FSCalendarTestSelectDate @@ -67,21 +82,22 @@ return _lunarDate.dayString; } -//- (BOOL)calendar:(FSCalendar *)calendar hasEventForDate:(NSDate *)date -//{ -// return date.fs_day % 5 == 0; -//} +- (BOOL)calendar:(FSCalendar *)calendar hasEventForDate:(NSDate *)date +{ + return [_datesWithEvent containsObject:[date fs_stringWithFormat:@"yyyy-MM-dd"]]; +} +/* - (NSDate *)minimumDateForCalendar:(FSCalendar *)calendar { - return [NSDate date]; + return [NSDate fs_dateWithYear:2015 month:2 day:1]; } - (NSDate *)maximumDateForCalendar:(FSCalendar *)calendar { return [[NSDate date] fs_dateByAddingMonths:3]; } - +*/ - (void)calendar:(FSCalendar *)calendar didDeselectDate:(NSDate *)date { @@ -92,7 +108,7 @@ - (BOOL)calendar:(FSCalendar *)calendar shouldSelectDate:(NSDate *)date { - BOOL shouldSelect = date.fs_day != 7; + BOOL shouldSelect = ![_datesShouldNotBeSelected containsObject:[date fs_stringWithFormat:@"yyyy/MM/dd"]]; if (!shouldSelect) { [[[UIAlertView alloc] initWithTitle:@"FSCalendar" message:[NSString stringWithFormat:@"FSCalendar delegate forbid %@ to be selected",[date fs_stringWithFormat:@"yyyy/MM/dd"]] diff --git a/FSCalendar/FSCalendar.m b/FSCalendar/FSCalendar.m index 0de3ef0..910c02e 100644 --- a/FSCalendar/FSCalendar.m +++ b/FSCalendar/FSCalendar.m @@ -73,7 +73,6 @@ @property (assign, nonatomic) BOOL needsAdjustingTextSize; @property (assign, nonatomic) BOOL needsReloadingSelectingDates; @property (assign, nonatomic) BOOL needsLayoutForWeekMode; -@property (assign, nonatomic) BOOL asyncronous; @property (assign, nonatomic) BOOL supressEvent; @property (assign, nonatomic) CGFloat preferedHeaderHeight; @property (assign, nonatomic) CGFloat preferedWeekdayHeight; @@ -171,7 +170,6 @@ _scrollEnabled = YES; _needsAdjustingViewFrame = YES; _needsAdjustingTextSize = YES; - _asyncronous = YES; _stickyHeaderMapTable = [NSMapTable weakToWeakObjectsMapTable]; UIView *contentView = [[UIView alloc] initWithFrame:CGRectZero]; @@ -303,7 +301,7 @@ if (_needsReloadingSelectingDates) { _needsReloadingSelectingDates = NO; - [self.selectedDates enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [_selectedDates enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [self selectDate:obj scrollToDate:NO]; }]; } @@ -479,10 +477,7 @@ } return NO; } - if (!self.allowsMultipleSelection && self.selectedDate) { - [self deselectDate:self.selectedDate]; - } - if ([collectionView.indexPathsForSelectedItems containsObject:indexPath] || [self.selectedDates containsObject:[self dateForIndexPath:indexPath]]) { + if ([collectionView.indexPathsForSelectedItems containsObject:indexPath] || [_selectedDates containsObject:[self dateForIndexPath:indexPath]]) { if (self.allowsMultipleSelection) { if ([self collectionView:collectionView shouldDeselectItemAtIndexPath:indexPath]) { [collectionView deselectItemAtIndexPath:indexPath animated:YES]; @@ -497,6 +492,11 @@ if (shouldSelect && cell.date && [self isDateInRange:cell.date] && !_supressEvent) { shouldSelect &= [self shouldSelectDate:cell.date]; } + if (shouldSelect) { + if (!self.allowsMultipleSelection && self.selectedDate) { + [self deselectDate:self.selectedDate]; + } + } return shouldSelect && [self isDateInRange:cell.date]; } @@ -874,7 +874,7 @@ - (NSDate *)selectedDate { - return self.selectedDates.lastObject; + return _selectedDates.lastObject; } - (NSArray *)selectedDates @@ -1011,7 +1011,6 @@ - (void)performScopeTransitionFromScope:(FSCalendarScope)fromScope toScope:(FSCalendarScope)toScope animated:(BOOL)animated { - _asyncronous = NO; NSInteger section = self.currentSection; void(^completion)(void) = ^{ switch (toScope) { @@ -1035,9 +1034,6 @@ _needsAdjustingViewFrame = YES; _needsReloadingSelectingDates = YES; [self setNeedsLayout]; - dispatch_async(dispatch_get_main_queue(), ^{ - _asyncronous = YES; - }); }; BOOL weekToMonth = fromScope == FSCalendarScopeWeek && toScope == FSCalendarScopeMonth; @@ -1422,7 +1418,7 @@ - (BOOL)isDateSelected:(NSDate *)date { - return [self.selectedDates containsObject:date] || [_collectionView.indexPathsForSelectedItems containsObject:[self indexPathForDate:date]]; + return [_selectedDates containsObject:date] || [_collectionView.indexPathsForSelectedItems containsObject:[self indexPathForDate:date]]; } - (BOOL)isDateInDifferentPage:(NSDate *)date @@ -1530,31 +1526,19 @@ - (void)invalidateAppearanceForCell:(FSCalendarCell *)cell { -#define m_async_background1 \ -\ - cell.preferedSelectionColor = [self preferedSelectionColorForDate:cell.date]; \ - cell.preferedTitleDefaultColor = [self preferedTitleDefaultColorForDate:cell.date]; \ - cell.preferedTitleSelectionColor = [self preferedTitleSelectionColorForDate:cell.date]; \ - if (cell.subtitle) { \ - cell.preferedSubtitleDefaultColor = [self preferedSubtitleDefaultColorForDate:cell.date]; \ - cell.preferedSubtitleSelectionColor = [self preferedSubtitleSelectionColorForDate:cell.date]; \ - } \ - if (cell.hasEvent) cell.preferedEventColor = [self preferedEventColorForDate:cell.date]; \ - cell.preferedBorderDefaultColor = [self preferedBorderDefaultColorForDate:cell.date]; \ - cell.preferedBorderSelectionColor = [self preferedBorderSelectionColorForDate:cell.date]; \ - cell.preferedCellShape = [self preferedCellShapeForDate:cell.date]; - - if (_asyncronous && !self.ibEditing) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - m_async_background1 - dispatch_async(dispatch_get_main_queue(), ^{ - [cell setNeedsLayout]; - }); - }); - } else { - m_async_background1 - [cell setNeedsLayout]; + cell.preferedSelectionColor = [self preferedSelectionColorForDate:cell.date]; + cell.preferedTitleDefaultColor = [self preferedTitleDefaultColorForDate:cell.date]; + cell.preferedTitleSelectionColor = [self preferedTitleSelectionColorForDate:cell.date]; + if (cell.subtitle) { + cell.preferedSubtitleDefaultColor = [self preferedSubtitleDefaultColorForDate:cell.date]; + cell.preferedSubtitleSelectionColor = [self preferedSubtitleSelectionColorForDate:cell.date]; } + if (cell.hasEvent) cell.preferedEventColor = [self preferedEventColorForDate:cell.date]; + cell.preferedBorderDefaultColor = [self preferedBorderDefaultColorForDate:cell.date]; + cell.preferedBorderSelectionColor = [self preferedBorderSelectionColorForDate:cell.date]; + cell.preferedCellShape = [self preferedCellShapeForDate:cell.date]; + + [cell setNeedsLayout]; } - (void)reloadDataForCell:(FSCalendarCell *)cell atIndexPath:(NSIndexPath *)indexPath @@ -1562,48 +1546,34 @@ cell.calendar = self; cell.appearance = _appearance; cell.date = [self dateForIndexPath:indexPath]; -#define m_async_background2 \ -\ - cell.image = [self imageForDate:cell.date]; \ - cell.subtitle = [self subtitleForDate:cell.date]; \ - cell.hasEvent = [self hasEventForDate:cell.date]; \ - cell.dateIsSelected = [self.selectedDates containsObject:cell.date]; \ - cell.dateIsToday = [cell.date fs_isEqualToDateForDay:_today]; \ -\ - switch (_scope) { \ - case FSCalendarScopeMonth: { \ - NSDate *month = [_minimumDate.fs_firstDayOfMonth fs_dateByAddingMonths:indexPath.section].fs_dateByIgnoringTimeComponents; \ - cell.dateIsPlaceholder = ![cell.date fs_isEqualToDateForMonth:month] || ![self isDateInRange:cell.date]; \ - if (cell.dateIsPlaceholder) { \ - cell.dateIsSelected &= _pagingEnabled; \ - cell.dateIsToday &= _pagingEnabled; \ - } \ - break; \ - } \ - case FSCalendarScopeWeek: { \ - if (_pagingEnabled) { \ - cell.dateIsPlaceholder = ![self isDateInRange:cell.date]; \ - } \ - break; \ - } \ - } -#define m_async_main2 \ - [self invalidateAppearanceForCell:cell]; \ - cell.selected = (cell.dateIsSelected && !cell.dateIsPlaceholder); \ - [cell setNeedsLayout]; - - if (_asyncronous && !self.ibEditing) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - m_async_background2 - dispatch_async(dispatch_get_main_queue(), ^{ - m_async_main2 - }); - }); - } else { - m_async_background2 - m_async_main2 + cell.image = [self imageForDate:cell.date]; + cell.hasEvent = [self hasEventForDate:cell.date]; + cell.subtitle = [self subtitleForDate:cell.date]; + cell.dateIsSelected = [_selectedDates containsObject:cell.date]; + cell.dateIsToday = [cell.date isEqual:_today]; + switch (_scope) { + case FSCalendarScopeMonth: { + NSDate *month = [_minimumDate.fs_firstDayOfMonth fs_dateByAddingMonths:indexPath.section].fs_dateByIgnoringTimeComponents; + cell.dateIsPlaceholder = ![cell.date fs_isEqualToDateForMonth:month] || ![self isDateInRange:cell.date]; + if (cell.dateIsPlaceholder) { + cell.dateIsSelected &= _pagingEnabled; + cell.dateIsToday &= _pagingEnabled; + } + break; + } \ + case FSCalendarScopeWeek: { + if (_pagingEnabled) { + cell.dateIsPlaceholder = ![self isDateInRange:cell.date]; + } + break; + } } + + [self invalidateAppearanceForCell:cell]; + cell.selected = (cell.dateIsSelected && !cell.dateIsPlaceholder); + [cell setNeedsLayout]; + } - (void)reloadVisibleCells diff --git a/FSCalendar/NSDate+FSExtension.h b/FSCalendar/NSDate+FSExtension.h index 4156dab..2975abd 100644 --- a/FSCalendar/NSDate+FSExtension.h +++ b/FSCalendar/NSDate+FSExtension.h @@ -59,3 +59,9 @@ @end +@interface NSDateFormatter (FSExtension) + ++ (instancetype)fs_sharedDateFormatter; + +@end + diff --git a/FSCalendar/NSDate+FSExtension.m b/FSCalendar/NSDate+FSExtension.m index 608f10d..f72d46f 100644 --- a/FSCalendar/NSDate+FSExtension.m +++ b/FSCalendar/NSDate+FSExtension.m @@ -134,7 +134,7 @@ + (instancetype)fs_dateFromString:(NSString *)string format:(NSString *)format { - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + NSDateFormatter *formatter = [NSDateFormatter fs_sharedDateFormatter]; formatter.dateFormat = format; return [formatter dateFromString:string]; } @@ -243,7 +243,7 @@ - (NSString *)fs_stringWithFormat:(NSString *)format { - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + NSDateFormatter *formatter = [NSDateFormatter fs_sharedDateFormatter]; formatter.dateFormat = format; return [formatter stringFromDate:self]; } @@ -286,3 +286,18 @@ @end + +@implementation NSDateFormatter (FSExtension) + ++ (instancetype)fs_sharedDateFormatter +{ + static id instance; + static dispatch_once_t fs_sharedDateFormatter_onceToken; + dispatch_once(&fs_sharedDateFormatter_onceToken, ^{ + instance = [[NSDateFormatter alloc] init]; + }); + return instance; +} + +@end + diff --git a/FSCalendar/NSString+FSExtension.m b/FSCalendar/NSString+FSExtension.m index e46ccb5..ce22fb0 100644 --- a/FSCalendar/NSString+FSExtension.m +++ b/FSCalendar/NSString+FSExtension.m @@ -7,12 +7,13 @@ // #import "NSString+FSExtension.h" +#import "NSDate+FSExtension.h" @implementation NSString (FSExtension) - (NSDate *)fs_dateWithFormat:(NSString *)format { - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + NSDateFormatter *formatter = [NSDateFormatter fs_sharedDateFormatter]; formatter.dateFormat = format; return [formatter dateFromString:self]; } From 9151f7a484a495ba8e86a330051cde1215de0373 Mon Sep 17 00:00:00 2001 From: WenchaoIOS Date: Sat, 7 Nov 2015 18:52:34 +0800 Subject: [PATCH 2/6] Forgot about setNeedsLayout.... Forgot about setNeedsLayout.... --- FSCalendar/FSCalendar.m | 3 ++- FSCalendar/NSDate+FSExtension.h | 8 ++++++++ FSCalendar/NSDate+FSExtension.m | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/FSCalendar/FSCalendar.m b/FSCalendar/FSCalendar.m index 910c02e..c54320d 100644 --- a/FSCalendar/FSCalendar.m +++ b/FSCalendar/FSCalendar.m @@ -262,7 +262,7 @@ weekdayHeight); }]; - _deliver.frame = _header.frame; + _deliver.frame = CGRectMake(_header.fs_left, _header.fs_top, _header.fs_width, headerHeight+weekdayHeight); _deliver.hidden = _header.hidden; if (_pagingEnabled) { @@ -1604,6 +1604,7 @@ if (cell.dateIsPlaceholder) { cell.dateIsSelected = NO; cell.selected = NO; + [cell setNeedsLayout]; } }]; } else { diff --git a/FSCalendar/NSDate+FSExtension.h b/FSCalendar/NSDate+FSExtension.h index 2975abd..74568d0 100644 --- a/FSCalendar/NSDate+FSExtension.h +++ b/FSCalendar/NSDate+FSExtension.h @@ -65,3 +65,11 @@ @end +@interface NSDateComponents (FSExtension) + ++ (instancetype)fs_sharedDateComponents; + +@end + + + diff --git a/FSCalendar/NSDate+FSExtension.m b/FSCalendar/NSDate+FSExtension.m index f72d46f..24d7049 100644 --- a/FSCalendar/NSDate+FSExtension.m +++ b/FSCalendar/NSDate+FSExtension.m @@ -301,3 +301,18 @@ @end +@implementation NSDateComponents (FSExtension) + ++ (instancetype)fs_sharedDateComponents +{ + static id instance; + static dispatch_once_t fs_sharedDateFormatter_onceToken; + dispatch_once(&fs_sharedDateFormatter_onceToken, ^{ + instance = [[NSDateComponents alloc] init]; + }); + return instance; +} + +@end + + From 5c3560f640f4a4efef7909e895cee8c4f7448373 Mon Sep 17 00:00:00 2001 From: WenchaoIOS Date: Sat, 7 Nov 2015 20:03:51 +0800 Subject: [PATCH 3/6] Clean selectedDate while selectedCell is beyond screen Clean selectedDate while selectedCell is beyond screen --- Example/StoryboardExampleViewController.m | 1 + FSCalendar/FSCalendar.m | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Example/StoryboardExampleViewController.m b/Example/StoryboardExampleViewController.m index 6cb083c..b80c0f8 100644 --- a/Example/StoryboardExampleViewController.m +++ b/Example/StoryboardExampleViewController.m @@ -130,6 +130,7 @@ - (void)calendarCurrentPageDidChange:(FSCalendar *)calendar { NSLog(@"did change to page %@",[calendar.currentPage fs_stringWithFormat:@"MMMM yyyy"]); + [calendar selectDate:calendar.currentPage]; } - (void)calendarCurrentScopeWillChange:(FSCalendar *)calendar animated:(BOOL)animated diff --git a/FSCalendar/FSCalendar.m b/FSCalendar/FSCalendar.m index c54320d..786becb 100644 --- a/FSCalendar/FSCalendar.m +++ b/FSCalendar/FSCalendar.m @@ -1214,13 +1214,14 @@ } else if (![_selectedDates containsObject:targetDate]){ // 手动选中日期时,需先反选已经选中的日期,但不触发事件 if (self.selectedDate && !self.allowsMultipleSelection) { - NSIndexPath *currentIndexPath = [self indexPathForDate:self.selectedDate]; + NSDate *selectedDate = self.selectedDate; + NSIndexPath *currentIndexPath = [self indexPathForDate:selectedDate]; [_collectionView deselectItemAtIndexPath:currentIndexPath animated:NO]; FSCalendarCell *cell = (FSCalendarCell *)[_collectionView cellForItemAtIndexPath:currentIndexPath]; cell.dateIsSelected = NO; [cell setNeedsLayout]; - [_selectedDates removeObject:cell.date]; - [self deselectCounterpartDate:cell.date]; + [_selectedDates removeObject:selectedDate]; + [self deselectCounterpartDate:selectedDate]; } [_collectionView selectItemAtIndexPath:targetIndexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone]; @@ -1601,7 +1602,7 @@ { if (self.floatingMode) { [_collectionView.visibleCells enumerateObjectsUsingBlock:^(FSCalendarCell *cell, NSUInteger index, BOOL *stop) { - if (cell.dateIsPlaceholder) { + if (cell.dateIsPlaceholder && cell.dateIsSelected) { cell.dateIsSelected = NO; cell.selected = NO; [cell setNeedsLayout]; From 6ad2658ec7f708ff5b7005989e2b85e91b2d8d0e Mon Sep 17 00:00:00 2001 From: WenchaoIOS Date: Sat, 7 Nov 2015 21:48:03 +0800 Subject: [PATCH 4/6] Should deselect selected counterpart date while clicking on counterpart date Should deselect selected counterpart date while clicking on counterpart date --- FSCalendar/FSCalendar.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FSCalendar/FSCalendar.m b/FSCalendar/FSCalendar.m index 786becb..0649ca1 100644 --- a/FSCalendar/FSCalendar.m +++ b/FSCalendar/FSCalendar.m @@ -1202,6 +1202,7 @@ if (_collectionView.indexPathsForSelectedItems.count && self.selectedDate && !self.allowsMultipleSelection) { NSIndexPath *currentIndexPath = [self indexPathForDate:self.selectedDate]; [_collectionView deselectItemAtIndexPath:currentIndexPath animated:YES]; + [self deselectCounterpartDate:self.selectedDate]; [self collectionView:_collectionView didDeselectItemAtIndexPath:currentIndexPath]; } if ([self collectionView:_collectionView shouldSelectItemAtIndexPath:targetIndexPath]) { @@ -1574,7 +1575,6 @@ [self invalidateAppearanceForCell:cell]; cell.selected = (cell.dateIsSelected && !cell.dateIsPlaceholder); [cell setNeedsLayout]; - } - (void)reloadVisibleCells From 0446f9febfefe6acca651f34767d60fef5349886 Mon Sep 17 00:00:00 2001 From: WenchaoIOS Date: Sun, 8 Nov 2015 09:13:09 +0800 Subject: [PATCH 5/6] NSDateComponents sharedInstance 1. Make NSDateComponents in NSDate+FSExtension.h single instance, avoid alloc every time using it. This makes performance better 2. Optimize other logic --- Example/AppDelegate.m | 2 - Example/StoryboardExampleViewController.m | 2 +- FSCalendar/FSCalendar.m | 90 ++++++++++++----------- FSCalendar/NSDate+FSExtension.m | 37 +++++++--- 4 files changed, 75 insertions(+), 56 deletions(-) diff --git a/Example/AppDelegate.m b/Example/AppDelegate.m index aac9a60..7eb4433 100644 --- a/Example/AppDelegate.m +++ b/Example/AppDelegate.m @@ -7,14 +7,12 @@ // #import "AppDelegate.h" -#import "FScalendar.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. - return YES; } diff --git a/Example/StoryboardExampleViewController.m b/Example/StoryboardExampleViewController.m index b80c0f8..5730b17 100644 --- a/Example/StoryboardExampleViewController.m +++ b/Example/StoryboardExampleViewController.m @@ -130,7 +130,7 @@ - (void)calendarCurrentPageDidChange:(FSCalendar *)calendar { NSLog(@"did change to page %@",[calendar.currentPage fs_stringWithFormat:@"MMMM yyyy"]); - [calendar selectDate:calendar.currentPage]; +// [calendar selectDate:calendar.currentPage]; } - (void)calendarCurrentScopeWillChange:(FSCalendar *)calendar animated:(BOOL)animated diff --git a/FSCalendar/FSCalendar.m b/FSCalendar/FSCalendar.m index 0649ca1..8aaeadc 100644 --- a/FSCalendar/FSCalendar.m +++ b/FSCalendar/FSCalendar.m @@ -477,19 +477,22 @@ } return NO; } - if ([collectionView.indexPathsForSelectedItems containsObject:indexPath] || [_selectedDates containsObject:[self dateForIndexPath:indexPath]]) { + NSDate *targetDate = [self dateForIndexPath:indexPath]; + if ([self isDateSelected:targetDate]) { + // 这个if几乎不会调用到 if (self.allowsMultipleSelection) { if ([self collectionView:collectionView shouldDeselectItemAtIndexPath:indexPath]) { [collectionView deselectItemAtIndexPath:indexPath animated:YES]; [self collectionView:collectionView didDeselectItemAtIndexPath:indexPath]; } } else { + // 点击了已经选择的日期,直接触发事件 [self didSelectDate:self.selectedDate]; } return NO; } BOOL shouldSelect = YES; - if (shouldSelect && cell.date && [self isDateInRange:cell.date] && !_supressEvent) { + if (cell.date && [self isDateInRange:cell.date] && !_supressEvent) { shouldSelect &= [self shouldSelectDate:cell.date]; } if (shouldSelect) { @@ -516,6 +519,7 @@ } NSDate *selectedDate = cell.date ?: [self dateForIndexPath:indexPath]; [_selectedDates removeObject:selectedDate]; + [self deselectCounterpartDate:selectedDate]; [self didDeselectDate:selectedDate]; } @@ -1182,60 +1186,62 @@ if (![self isDateInRange:date]) { [NSException raise:@"selectedDate out of range" format:@""]; } - NSDate *targetDate = [date fs_daysFrom:_minimumDate] < 0 ? _minimumDate.copy : date; - targetDate = [targetDate fs_daysFrom:_maximumDate] > 0 ? _maximumDate.copy : targetDate; - targetDate = targetDate.fs_dateByIgnoringTimeComponents; + NSDate *targetDate = date.fs_dateByIgnoringTimeComponents; NSIndexPath *targetIndexPath = [self indexPathForDate:targetDate]; BOOL shouldSelect = !_supressEvent; + // 跨月份点击 if (forPlaceholder) { - // 跨月份选中日期,需要触发各类事件 - if (self.allowsMultipleSelection && [self isDateSelected:targetDate]) { - // 在多选模式下,点击了已经选中的跨月日期 - BOOL shouldDeselect = [self shouldDeselectDate:targetDate]; - if (!shouldDeselect) { + if (self.allowsMultipleSelection) { + // 处理多选模式 + if ([self isDateSelected:targetDate]) { + BOOL shouldDeselect = [self shouldDeselectDate:targetDate]; + if (!shouldDeselect) { + return; + } + } else { + shouldSelect &= [self shouldSelectDate:targetDate]; + if (!shouldSelect) { + return; + } + } + } else { + // 处理单选模式 + shouldSelect &= [self shouldSelectDate:targetDate]; + if (shouldSelect) { + if ([self isDateSelected:targetDate]) { + [self didSelectDate:targetDate]; + } else { + NSDate *selectedDate = self.selectedDate; + if (selectedDate) { + [self deselectDate:selectedDate]; + } + [_collectionView selectItemAtIndexPath:targetIndexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone]; + [self collectionView:_collectionView didSelectItemAtIndexPath:targetIndexPath]; + } + } else { return; } } - shouldSelect &= [self shouldSelectDate:targetDate]; - if (shouldSelect && ![self isDateSelected:targetDate]) { - if (_collectionView.indexPathsForSelectedItems.count && self.selectedDate && !self.allowsMultipleSelection) { - NSIndexPath *currentIndexPath = [self indexPathForDate:self.selectedDate]; - [_collectionView deselectItemAtIndexPath:currentIndexPath animated:YES]; - [self deselectCounterpartDate:self.selectedDate]; - [self collectionView:_collectionView didDeselectItemAtIndexPath:currentIndexPath]; - } - if ([self collectionView:_collectionView shouldSelectItemAtIndexPath:targetIndexPath]) { - [_collectionView selectItemAtIndexPath:targetIndexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone]; - [self collectionView:_collectionView didSelectItemAtIndexPath:targetIndexPath]; - } - } else { - [self didSelectDate:targetDate]; - } - } else if (![_selectedDates containsObject:targetDate]){ - // 手动选中日期时,需先反选已经选中的日期,但不触发事件 + + } else if (![self isDateSelected:targetDate]){ + // 调用代码选中未选中日期 if (self.selectedDate && !self.allowsMultipleSelection) { - NSDate *selectedDate = self.selectedDate; - NSIndexPath *currentIndexPath = [self indexPathForDate:selectedDate]; - [_collectionView deselectItemAtIndexPath:currentIndexPath animated:NO]; - FSCalendarCell *cell = (FSCalendarCell *)[_collectionView cellForItemAtIndexPath:currentIndexPath]; - cell.dateIsSelected = NO; - [cell setNeedsLayout]; - [_selectedDates removeObject:selectedDate]; - [self deselectCounterpartDate:selectedDate]; + [self deselectDate:self.selectedDate]; } [_collectionView selectItemAtIndexPath:targetIndexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone]; - FSCalendarCell *cell = (FSCalendarCell *)[_collectionView cellForItemAtIndexPath:targetIndexPath]; [cell performSelecting]; [self enqueueSelectedDate:targetDate]; [self selectCounterpartDate:targetDate]; } else if (![_collectionView.indexPathsForSelectedItems containsObject:targetIndexPath]) { + // 调用代码选中以选中日期 [_collectionView selectItemAtIndexPath:targetIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; } if (scrollToDate) { + // 如果跨月份点击日期,并且该日期不应该选中,则不跳转页面,其他情况均跳转 if (forPlaceholder && !shouldSelect) { return; } @@ -1275,7 +1281,7 @@ } } - if (_pagingEnabled) { + if (!self.floatingMode) { switch (_collectionViewLayout.scrollDirection) { case UICollectionViewScrollDirectionVertical: { @@ -1292,16 +1298,18 @@ } } else { - CGRect itemFrame = [_collectionViewLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:scrollOffset]].frame; - if (self.floatingMode && CGRectEqualToRect(itemFrame, CGRectZero)) { + // 全屏模式中,切换页面时需要将该月份提升到视图最上方 + CGRect headerFrame = [_collectionViewLayout layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:scrollOffset]].frame; + if (CGSizeEqualToSize(headerFrame.size, CGSizeZero)) { + // 如果在loadView或者viewDidLoad中调用需要切换月份的方法, 这时UICollectionView并没有准备好自己的单元格和空间大小,这时不能直接调用setContentOffset,而是等到在layoutSubviews之后再去调用 _currentPage = targetDate; _needsAdjustingMonthPosition = YES; [self setNeedsLayout]; } else { - CGRect headerFrame = [_collectionViewLayout layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:scrollOffset]].frame; CGPoint targetOffset = CGPointMake(0, MIN(headerFrame.origin.y,_collectionView.contentSize.height-_collectionView.fs_bottom)); [_collectionView setContentOffset:targetOffset animated:animated]; } + } if (_header && !animated) { @@ -1335,7 +1343,7 @@ } [self didChangeValueForKey:@"currentPage"]; [self scrollToDate:_currentPage animated:animated]; - } else if (!_pagingEnabled) { + } else { [self scrollToDate:date.fs_firstDayOfMonth animated:animated]; } } diff --git a/FSCalendar/NSDate+FSExtension.m b/FSCalendar/NSDate+FSExtension.m index 24d7049..8740785 100644 --- a/FSCalendar/NSDate+FSExtension.m +++ b/FSCalendar/NSDate+FSExtension.m @@ -99,11 +99,12 @@ { NSCalendar *calendar = [NSCalendar fs_sharedCalendar]; NSDateComponents *weekdayComponents = [calendar components:NSCalendarUnitWeekday fromDate:self]; - NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init]; + NSDateComponents *componentsToSubtract = [NSDateComponents fs_sharedDateComponents]; componentsToSubtract.day = - (weekdayComponents.weekday - calendar.firstWeekday); NSDate *beginningOfWeek = [calendar dateByAddingComponents:componentsToSubtract toDate:self options:0]; NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:beginningOfWeek]; beginningOfWeek = [calendar dateFromComponents:components]; + componentsToSubtract.day = NSIntegerMax; return beginningOfWeek; } @@ -142,19 +143,25 @@ + (instancetype)fs_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day { NSCalendar *calendar = [NSCalendar fs_sharedCalendar]; - NSDateComponents *components = [[NSDateComponents alloc] init]; + NSDateComponents *components = [NSDateComponents fs_sharedDateComponents]; components.year = year; components.month = month; components.day = day; - return [calendar dateFromComponents:components]; + NSDate *date = [calendar dateFromComponents:components]; + components.year = NSIntegerMax; + components.month = NSIntegerMax; + components.day = NSIntegerMax; + return date; } - (NSDate *)fs_dateByAddingYears:(NSInteger)years { NSCalendar *calendar = [NSCalendar fs_sharedCalendar]; - NSDateComponents *components = [[NSDateComponents alloc] init]; + NSDateComponents *components = [NSDateComponents fs_sharedDateComponents]; components.year = years; - return [calendar dateByAddingComponents:components toDate:self options:0]; + NSDate *date = [calendar dateByAddingComponents:components toDate:self options:0]; + components.year = NSIntegerMax; + return date; } - (NSDate *)fs_dateBySubtractingYears:(NSInteger)years @@ -165,9 +172,11 @@ - (NSDate *)fs_dateByAddingMonths:(NSInteger)months { NSCalendar *calendar = [NSCalendar fs_sharedCalendar]; - NSDateComponents *components = [[NSDateComponents alloc] init]; + NSDateComponents *components = [NSDateComponents fs_sharedDateComponents]; components.month = months; - return [calendar dateByAddingComponents:components toDate:self options:0]; + NSDate *date = [calendar dateByAddingComponents:components toDate:self options:0]; + components.month = NSIntegerMax; + return date; } - (NSDate *)fs_dateBySubtractingMonths:(NSInteger)months @@ -178,9 +187,11 @@ - (NSDate *)fs_dateByAddingWeeks:(NSInteger)weeks { NSCalendar *calendar = [NSCalendar fs_sharedCalendar]; - NSDateComponents *components = [[NSDateComponents alloc] init]; + NSDateComponents *components = [NSDateComponents fs_sharedDateComponents]; components.weekOfYear = weeks; - return [calendar dateByAddingComponents:components toDate:self options:0]; + NSDate *date = [calendar dateByAddingComponents:components toDate:self options:0]; + components.weekOfYear = NSIntegerMax; + return date; } -(NSDate *)fs_dateBySubtractingWeeks:(NSInteger)weeks @@ -191,9 +202,11 @@ - (NSDate *)fs_dateByAddingDays:(NSInteger)days { NSCalendar *calendar = [NSCalendar fs_sharedCalendar]; - NSDateComponents *components = [[NSDateComponents alloc] init]; - [components setDay:days]; - return [calendar dateByAddingComponents:components toDate:self options:0]; + NSDateComponents *components = [NSDateComponents fs_sharedDateComponents]; + components.day = days; + NSDate *date = [calendar dateByAddingComponents:components toDate:self options:0]; + components.day = NSIntegerMax; + return date; } - (NSDate *)fs_dateBySubtractingDays:(NSInteger)days From a8ea508121bcb3874650db51dba48bf45f8fc367 Mon Sep 17 00:00:00 2001 From: WenchaoIOS Date: Sun, 8 Nov 2015 09:49:18 +0800 Subject: [PATCH 6/6] Fix double click problem Fix double click problem --- FSCalendar/FSCalendar.m | 42 +++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/FSCalendar/FSCalendar.m b/FSCalendar/FSCalendar.m index 8aaeadc..131f90a 100644 --- a/FSCalendar/FSCalendar.m +++ b/FSCalendar/FSCalendar.m @@ -1236,7 +1236,7 @@ [self selectCounterpartDate:targetDate]; } else if (![_collectionView.indexPathsForSelectedItems containsObject:targetIndexPath]) { - // 调用代码选中以选中日期 + // 调用代码选中已选中日期 [_collectionView selectItemAtIndexPath:targetIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; } @@ -1320,28 +1320,30 @@ - (void)scrollToPageForDate:(NSDate *)date animated:(BOOL)animated { - if (!_collectionView.tracking && !_collectionView.decelerating) { - if (!self.floatingMode && [self isDateInDifferentPage:date]) { - [self willChangeValueForKey:@"currentPage"]; - switch (_scope) { - case FSCalendarScopeMonth: { - _currentPage = date.fs_firstDayOfMonth; - break; + if (!_collectionView.tracking) { + if (!self.floatingMode) { + if ([self isDateInDifferentPage:date]) { + [self willChangeValueForKey:@"currentPage"]; + switch (_scope) { + case FSCalendarScopeMonth: { + _currentPage = date.fs_firstDayOfMonth; + break; + } + case FSCalendarScopeWeek: { + _currentPage = date.fs_firstDayOfWeek; + break; + } + default: { + break; + } } - case FSCalendarScopeWeek: { - _currentPage = date.fs_firstDayOfWeek; - break; - } - default: { - break; + if (!_supressEvent && !CGSizeEqualToSize(_collectionView.frame.size, CGSizeZero)) { + _supressEvent = YES; + [self currentPageDidChange]; + _supressEvent = NO; } + [self didChangeValueForKey:@"currentPage"]; } - if (!_supressEvent && !CGSizeEqualToSize(_collectionView.frame.size, CGSizeZero)) { - _supressEvent = YES; - [self currentPageDidChange]; - _supressEvent = NO; - } - [self didChangeValueForKey:@"currentPage"]; [self scrollToDate:_currentPage animated:animated]; } else { [self scrollToDate:date.fs_firstDayOfMonth animated:animated];