From 8bbc309c359138e4c19ff978e004e6a71cb75379 Mon Sep 17 00:00:00 2001 From: dingwenchao Date: Wed, 9 Sep 2015 09:59:36 +0800 Subject: [PATCH] Multiple Selection Use `allowsMultipleSelection` to enable this feature --- Example/Base.lproj/Main_iPhone.storyboard | 20 ++ Example/FSCalendar.xcodeproj/project.pbxproj | 20 +- .../FSCalendarScopeExampleViewController.m | 13 ++ Example/FSTableViewController.m | 12 +- Example/LoadViewExampleViewController.m | 11 +- Example/MultipleSelectionViewController.h | 17 ++ Example/MultipleSelectionViewController.m | 47 +++++ Example/StoryboardExampleViewController.m | 3 +- Example/ViewDidLoadExampleViewController.m | 12 +- FSCalendar.podspec | 6 +- FSCalendar/FSCalendar.h | 22 ++- FSCalendar/FSCalendar.m | 184 +++++++++++------- FSCalendar/FSCalendarCell.m | 9 +- README.md | 10 +- 14 files changed, 287 insertions(+), 99 deletions(-) create mode 100644 Example/MultipleSelectionViewController.h create mode 100644 Example/MultipleSelectionViewController.m diff --git a/Example/Base.lproj/Main_iPhone.storyboard b/Example/Base.lproj/Main_iPhone.storyboard index 544ba7e..2ae68f0 100644 --- a/Example/Base.lproj/Main_iPhone.storyboard +++ b/Example/Base.lproj/Main_iPhone.storyboard @@ -33,6 +33,26 @@ + + + + + + + + + + diff --git a/Example/FSCalendar.xcodeproj/project.pbxproj b/Example/FSCalendar.xcodeproj/project.pbxproj index 1f1946e..8430eab 100644 --- a/Example/FSCalendar.xcodeproj/project.pbxproj +++ b/Example/FSCalendar.xcodeproj/project.pbxproj @@ -22,7 +22,6 @@ 30B0BAD61B8D8E23004B9476 /* NSDate+FSExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B0BACC1B8D8E22004B9476 /* NSDate+FSExtension.m */; }; 30B0BAD71B8D8E23004B9476 /* UIView+FSExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B0BACE1B8D8E22004B9476 /* UIView+FSExtension.m */; }; 30B0BAF41B8D9AC1004B9476 /* FSCalendar+IBExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B0BABE1B8D8E22004B9476 /* FSCalendar+IBExtension.m */; }; - 30B0BAF51B8D9AC1004B9476 /* FSCalendar.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B0BAC01B8D8E22004B9476 /* FSCalendar.m */; }; 30B0BAF61B8D9AC1004B9476 /* FSCalendarAppearance.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B0BAC21B8D8E22004B9476 /* FSCalendarAppearance.m */; }; 30B0BAF71B8D9AC1004B9476 /* FSCalendarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B0BAC41B8D8E22004B9476 /* FSCalendarCell.m */; }; 30B0BAF81B8D9AC1004B9476 /* FSCalendarHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B0BAC71B8D8E22004B9476 /* FSCalendarHeader.m */; }; @@ -38,6 +37,8 @@ 30B0BB051B8D9B6D004B9476 /* FSCalendarHeaderTouchDeliver.h in Headers */ = {isa = PBXBuildFile; fileRef = 30B0BAC81B8D8E22004B9476 /* FSCalendarHeaderTouchDeliver.h */; settings = {ATTRIBUTES = (Private, ); }; }; 30B0BB061B8D9B6D004B9476 /* NSDate+FSExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 30B0BACB1B8D8E22004B9476 /* NSDate+FSExtension.h */; settings = {ATTRIBUTES = (Private, ); }; }; 30B0BB071B8D9B6D004B9476 /* UIView+FSExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 30B0BACD1B8D8E22004B9476 /* UIView+FSExtension.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 30F5D8561B9FC33400C1C201 /* FSCalendar.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B0BAC01B8D8E22004B9476 /* FSCalendar.m */; }; + 30F5D85A1B9FC4BB00C1C201 /* MultipleSelectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 30F5D8591B9FC4BB00C1C201 /* MultipleSelectionViewController.m */; }; EE52AE181B91E68A00016662 /* FSCalendarScopeExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE52AE171B91E68A00016662 /* FSCalendarScopeExampleViewController.m */; }; EE638CB21B89DB450006DD1A /* LunarDateFormatter.strings in Resources */ = {isa = PBXBuildFile; fileRef = EE638C911B89DB450006DD1A /* LunarDateFormatter.strings */; }; EE638CB31B89DB450006DD1A /* libLunar.c in Sources */ = {isa = PBXBuildFile; fileRef = EE638C941B89DB450006DD1A /* libLunar.c */; }; @@ -113,6 +114,8 @@ 30B0BACC1B8D8E22004B9476 /* NSDate+FSExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+FSExtension.m"; sourceTree = ""; }; 30B0BACD1B8D8E22004B9476 /* UIView+FSExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+FSExtension.h"; sourceTree = ""; }; 30B0BACE1B8D8E22004B9476 /* UIView+FSExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+FSExtension.m"; sourceTree = ""; }; + 30F5D8581B9FC4BB00C1C201 /* MultipleSelectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultipleSelectionViewController.h; sourceTree = SOURCE_ROOT; }; + 30F5D8591B9FC4BB00C1C201 /* MultipleSelectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultipleSelectionViewController.m; sourceTree = SOURCE_ROOT; }; EE0D7FC71B89C5D3003C287B /* FSCalendarExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FSCalendarExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; EE0D7FCB1B89C5D3003C287B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; EE52AE161B91E68A00016662 /* FSCalendarScopeExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSCalendarScopeExampleViewController.h; sourceTree = SOURCE_ROOT; }; @@ -229,6 +232,15 @@ name = "Supporting Files"; sourceTree = ""; }; + 30F5D8571B9FC48C00C1C201 /* MultipleSelection Example */ = { + isa = PBXGroup; + children = ( + 30F5D8581B9FC4BB00C1C201 /* MultipleSelectionViewController.h */, + 30F5D8591B9FC4BB00C1C201 /* MultipleSelectionViewController.m */, + ); + name = "MultipleSelection Example"; + sourceTree = ""; + }; EE0D7FBE1B89C5D3003C287B = { isa = PBXGroup; children = ( @@ -257,6 +269,7 @@ EE638CD01B89DBE90006DD1A /* AppDelegate.m */, EE638CD11B89DBE90006DD1A /* FSTableViewController.h */, EE638CD21B89DBE90006DD1A /* FSTableViewController.m */, + 30F5D8571B9FC48C00C1C201 /* MultipleSelection Example */, EE52AE151B91E65D00016662 /* FSCalendarScope Example */, EE0D80241B89C868003C287B /* Storyboard Example */, EE0D80231B89C853003C287B /* LoadView Example */, @@ -502,6 +515,7 @@ 30B0BAD01B8D8E23004B9476 /* FSCalendar.m in Sources */, EE638CD31B89DBE90006DD1A /* AppDelegate.m in Sources */, 3092253B1B905C4300123031 /* FSCalendarConstance.m in Sources */, + 30F5D85A1B9FC4BB00C1C201 /* MultipleSelectionViewController.m in Sources */, EE638CBC1B89DB450006DD1A /* SSHolidayWest.m in Sources */, EE638CBB1B89DB450006DD1A /* SSHolidayUS.m in Sources */, 30B0BAD21B8D8E23004B9476 /* FSCalendarCell.m in Sources */, @@ -533,15 +547,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 30F5D8561B9FC33400C1C201 /* FSCalendar.m in Sources */, 30B0BAF41B8D9AC1004B9476 /* FSCalendar+IBExtension.m in Sources */, - 30B0BAF51B8D9AC1004B9476 /* FSCalendar.m in Sources */, - EE89AFA01B9162B90001B657 /* NSString+FSExtension.m in Sources */, 30B0BAF61B8D9AC1004B9476 /* FSCalendarAppearance.m in Sources */, 30B0BAF71B8D9AC1004B9476 /* FSCalendarCell.m in Sources */, 30B0BAF81B8D9AC1004B9476 /* FSCalendarHeader.m in Sources */, 30B0BAF91B8D9AC1004B9476 /* FSCalendarHeaderTouchDeliver.m in Sources */, 3092253C1B905C4300123031 /* FSCalendarConstance.m in Sources */, 30B0BAFA1B8D9AC1004B9476 /* NSDate+FSExtension.m in Sources */, + EE89AFA01B9162B90001B657 /* NSString+FSExtension.m in Sources */, 30B0BAFB1B8D9AC1004B9476 /* UIView+FSExtension.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/FSCalendarScopeExampleViewController.m b/Example/FSCalendarScopeExampleViewController.m index c5d62b5..9877910 100644 --- a/Example/FSCalendarScopeExampleViewController.m +++ b/Example/FSCalendarScopeExampleViewController.m @@ -7,6 +7,7 @@ // #import "FSCalendarScopeExampleViewController.h" +#import "NSDate+FSExtension.h" @implementation FSCalendarScopeExampleViewController @@ -23,6 +24,18 @@ [self.view layoutIfNeeded]; } +- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date +{ + NSLog(@"did select date %@",[date fs_stringWithFormat:@"yyyy/MM/dd"]); + + NSMutableArray *selectedDates = [NSMutableArray arrayWithCapacity:calendar.selectedDates.count]; + [calendar.selectedDates enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [selectedDates addObject:[obj fs_stringWithFormat:@"yyyy/MM/dd"]]; + }]; + NSLog(@"selected dates is %@",selectedDates); + +} + #pragma mark - - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView diff --git a/Example/FSTableViewController.m b/Example/FSTableViewController.m index 1a7cddd..13cb519 100644 --- a/Example/FSTableViewController.m +++ b/Example/FSTableViewController.m @@ -9,6 +9,7 @@ #import "FSTableViewController.h" #import "LoadViewExampleViewController.h" #import "ViewDidLoadExampleViewController.h" +#import "MultipleSelectionViewController.h" @implementation FSTableViewController @@ -22,24 +23,25 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { + MultipleSelectionViewController *viewController = [[MultipleSelectionViewController alloc] init]; + [self.navigationController pushViewController:viewController animated:YES]; + } else if (indexPath.row == 1) { // FSCalendarScope Example return; - } else if (indexPath.row == 1) { + } else if (indexPath.row == 2) { // Storyboard Example return; - } else if (indexPath.row == 2) { + } else if (indexPath.row == 3) { // LoadView Example LoadViewExampleViewController *viewController = [[LoadViewExampleViewController alloc] init]; - viewController.title = @"FSCalendar"; [self.navigationController pushViewController:viewController animated:YES]; - } else if (indexPath.row == 3) { + } else if (indexPath.row == 4) { // ViewDidLoad Example ViewDidLoadExampleViewController *viewController = [[ViewDidLoadExampleViewController alloc] init]; - viewController.title = @"FSCalendar"; [self.navigationController pushViewController:viewController animated:YES]; } } diff --git a/Example/LoadViewExampleViewController.m b/Example/LoadViewExampleViewController.m index 4ad33e2..0aec676 100644 --- a/Example/LoadViewExampleViewController.m +++ b/Example/LoadViewExampleViewController.m @@ -16,6 +16,15 @@ NSLog(@"%@:%s", self.class.description, __FUNCTION__); } +- (instancetype)init +{ + self = [super init]; + if (self) { + self.title = @"FSCalendar"; + } + return self; +} + - (void)loadView { UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; @@ -26,7 +35,7 @@ calendar.dataSource = self; calendar.delegate = self; calendar.scrollDirection = FSCalendarScrollDirectionVertical; - calendar.selectedDate = [NSDate fs_dateWithYear:2015 month:2 day:1]; + [calendar selectDate:[NSDate fs_dateWithYear:2015 month:2 day:1]]; [view addSubview:calendar]; self.calendar = calendar; } diff --git a/Example/MultipleSelectionViewController.h b/Example/MultipleSelectionViewController.h new file mode 100644 index 0000000..4f41d72 --- /dev/null +++ b/Example/MultipleSelectionViewController.h @@ -0,0 +1,17 @@ +// +// MultipleSelectionViewController.h +// FSCalendar +// +// Created by dingwenchao on 9/9/15. +// Copyright (c) 2015 wenchaoios. All rights reserved. +// + +#import +#import "FSCalendar.h" +#import "NSDate+FSExtension.h" + +@interface MultipleSelectionViewController : UIViewController + +@property (weak, nonatomic) FSCalendar *calendar; + +@end diff --git a/Example/MultipleSelectionViewController.m b/Example/MultipleSelectionViewController.m new file mode 100644 index 0000000..a2883ec --- /dev/null +++ b/Example/MultipleSelectionViewController.m @@ -0,0 +1,47 @@ +// +// MultipleSelectionViewController.m +// FSCalendar +// +// Created by dingwenchao on 9/9/15. +// Copyright (c) 2015 wenchaoios. All rights reserved. +// + +#import "MultipleSelectionViewController.h" + + +@implementation MultipleSelectionViewController + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.title = @"FSCalendar"; + } + return self; +} + +- (void)loadView +{ + UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds]; + view.backgroundColor = [UIColor colorWithRed:0.95 green:0.95 blue:0.95 alpha:1.0]; + self.view = view; + + FSCalendar *calendar = [[FSCalendar alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.navigationController.navigationBar.frame), view.bounds.size.width, 300)]; + calendar.dataSource = self; + calendar.delegate = self; + calendar.backgroundColor = [UIColor whiteColor]; + calendar.allowsMultipleSelection = YES; + [self.view addSubview:calendar]; + self.calendar = calendar; +} + +- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date +{ + NSMutableArray *selectedDates = [NSMutableArray arrayWithCapacity:calendar.selectedDates.count]; + [calendar.selectedDates enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [selectedDates addObject:[obj fs_stringWithFormat:@"yyyy/MM/dd"]]; + }]; + NSLog(@"selected dates is %@",selectedDates); +} + +@end diff --git a/Example/StoryboardExampleViewController.m b/Example/StoryboardExampleViewController.m index bf83105..d685de9 100644 --- a/Example/StoryboardExampleViewController.m +++ b/Example/StoryboardExampleViewController.m @@ -99,6 +99,7 @@ - (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date { NSLog(@"did select date %@",[date fs_stringWithFormat:@"yyyy/MM/dd"]); + } - (void)calendarCurrentMonthDidChange:(FSCalendar *)calendar @@ -192,7 +193,7 @@ - (void)setSelectedDate:(NSDate *)selectedDate { - _calendar.selectedDate = selectedDate; + [_calendar selectDate:selectedDate]; } - (void)setFirstWeekday:(NSUInteger)firstWeekday diff --git a/Example/ViewDidLoadExampleViewController.m b/Example/ViewDidLoadExampleViewController.m index 5c0e9a0..505d8f1 100644 --- a/Example/ViewDidLoadExampleViewController.m +++ b/Example/ViewDidLoadExampleViewController.m @@ -16,6 +16,15 @@ NSLog(@"%@:%s", self.class.description, __FUNCTION__); } +- (instancetype)init +{ + self = [super init]; + if (self) { + self.title = @"FSCalendar"; + } + return self; +} + - (void)loadView { UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; @@ -30,8 +39,7 @@ FSCalendar *calendar = [[FSCalendar alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, 300)]; calendar.dataSource = self; calendar.delegate = self; -// calendar.flow = FSCalendarFlowVertical; - calendar.selectedDate = [NSDate fs_dateWithYear:2015 month:2 day:1]; + [calendar selectDate:[NSDate fs_dateWithYear:2015 month:2 day:1]]; [self.view addSubview:calendar]; self.calendar = calendar; diff --git a/FSCalendar.podspec b/FSCalendar.podspec index 3cdf683..f1cc845 100644 --- a/FSCalendar.podspec +++ b/FSCalendar.podspec @@ -1,10 +1,10 @@ Pod::Spec.new do |s| s.name = "FSCalendar" - s.version = "1.1.4" - s.summary = "A powerful calendar which supports Appearance, Infinite Scrolling and Subtitle" + s.version = "1.2.0" + s.summary = "The fastest, most beautiful and most convenient calendar control, https://www.cocoacontrols.com/controls/fscalendar" - s.homepage = "https://github.com/f33chobits/FSCalendar" + s.homepage = "https://github.com/WenchaoIOS/FSCalendar" s.screenshots = "https://cloud.githubusercontent.com/assets/5186464/6655324/213a814a-cb36-11e4-9add-f80515a83291.png","https://cloud.githubusercontent.com/assets/5186464/6652191/f11d5242-caa1-11e4-9cc2-8a7c0cc9ef02.gif","https://cloud.githubusercontent.com/assets/5186464/6652193/19e7f92a-caa2-11e4-92af-0639dc0c2d79.gif","https://cloud.githubusercontent.com/assets/5186464/6680012/4af05080-cc8c-11e4-863a-59cd3507192d.gif" s.license = 'MIT' s.author = { "Wenchao Ding" => "f33chobits@gmail.com" } diff --git a/FSCalendar/FSCalendar.h b/FSCalendar/FSCalendar.h index 2c6088c..70e4659 100644 --- a/FSCalendar/FSCalendar.h +++ b/FSCalendar/FSCalendar.h @@ -9,6 +9,8 @@ #import #import "FSCalendarAppearance.h" +#define FSCalendarDeprecated(message) __attribute((deprecated(message))) + //! Project version number for FSCalendar. FOUNDATION_EXPORT double FSCalendarVersionNumber; @@ -17,7 +19,7 @@ FOUNDATION_EXPORT const unsigned char FSCalendarVersionString[]; @class FSCalendar; -__attribute((deprecated("use \'FSCalendarScrollDirection\' instead"))) +FSCalendarDeprecated("use \'FSCalendarScrollDirection\' instead") typedef NS_ENUM(NSInteger, FSCalendarFlow) { FSCalendarFlowVertical, FSCalendarFlowHorizontal @@ -50,7 +52,7 @@ typedef NS_ENUM(NSInteger, FSCalendarCellState) { - (void)calendarCurrentPageDidChange:(FSCalendar *)calendar; - (void)calendarCurrentScopeWillChange:(FSCalendar *)calendar animated:(BOOL)animated; -- (void)calendarCurrentMonthDidChange:(FSCalendar *)calendar __attribute((deprecated("use \'calendarCurrentPageDidChange\' instead"))); +- (void)calendarCurrentMonthDidChange:(FSCalendar *)calendar FSCalendarDeprecated("use \'calendarCurrentPageDidChange\' instead"); @end @@ -72,7 +74,6 @@ IB_DESIGNABLE @property (weak, nonatomic) IBOutlet id dataSource; @property (strong, nonatomic) NSDate *today; -@property (strong, nonatomic) NSDate *selectedDate; @property (strong, nonatomic) NSDate *currentPage; @property (strong, nonatomic) NSLocale *locale; @@ -80,24 +81,33 @@ IB_DESIGNABLE @property (assign, nonatomic) FSCalendarScope scope; @property (assign, nonatomic) IBInspectable NSUInteger firstWeekday; @property (assign, nonatomic) IBInspectable CGFloat headerHeight; +@property (assign, nonatomic) IBInspectable BOOL allowsMultipleSelection; @property (readonly, nonatomic) FSCalendarAppearance *appearance; @property (readonly, nonatomic) NSDate *minimumDate; @property (readonly, nonatomic) NSDate *maximumDate; +@property (readonly, nonatomic) NSDate *selectedDate; +@property (readonly, nonatomic) NSArray *selectedDates; + - (void)reloadData; - (CGSize)sizeThatFits:(CGSize)size; -- (void)setSelectedDate:(NSDate *)selectedDate animate:(BOOL)animate; - (void)setScope:(FSCalendarScope)scope animated:(BOOL)animated; +- (void)selectDate:(NSDate *)date; +- (void)selectDate:(NSDate *)date scrollToDate:(BOOL)scrollToDate; + @end @interface FSCalendar (Deprecated) -@property (strong, nonatomic) NSDate *currentMonth __attribute((deprecated("use \'currentPage\' instead"))); -@property (assign, nonatomic) FSCalendarFlow flow __attribute((deprecated("use \'scrollDirection\' instead"))); +@property (strong, nonatomic) NSDate *currentMonth FSCalendarDeprecated("use \'currentPage\' instead"); +@property (assign, nonatomic) FSCalendarFlow flow FSCalendarDeprecated("use \'scrollDirection\' instead"); + +- (void)setSelectedDate:(NSDate *)selectedDate FSCalendarDeprecated("use \'selectDate:\' instead"); +- (void)setSelectedDate:(NSDate *)selectedDate animate:(BOOL)animate FSCalendarDeprecated("use \'selectDate:scrollToDate:\' instead"); @end diff --git a/FSCalendar/FSCalendar.m b/FSCalendar/FSCalendar.m index e1bda1c..b3fa2a9 100644 --- a/FSCalendar/FSCalendar.m +++ b/FSCalendar/FSCalendar.m @@ -15,6 +15,7 @@ #import "NSString+FSExtension.h" #import "FSCalendarDynamicHeader.h" #import "FSCalendarHeaderTouchDeliver.h" + #import "FSCalendarConstance.h" static BOOL FSCalendarInInterfaceBuilder = NO; @@ -36,6 +37,7 @@ static BOOL FSCalendarInInterfaceBuilder = NO; @interface FSCalendar () { FSCalendarAppearance *_appearance; + NSMutableArray *_selectedDates; NSDate *_minimumDate; NSDate *_maximumDate; } @@ -58,6 +60,7 @@ static BOOL FSCalendarInInterfaceBuilder = NO; @property (assign, nonatomic) BOOL needsAdjustingMonthPosition; @property (assign, nonatomic) BOOL needsAdjustingViewFrame; @property (assign, nonatomic) BOOL needsAdjustingTextSize; +@property (assign, nonatomic) BOOL needsReloadingSelectingDates; @property (assign, nonatomic) BOOL supressEvent; @property (readonly, nonatomic) NSInteger currentSection; @@ -73,7 +76,7 @@ static BOOL FSCalendarInInterfaceBuilder = NO; - (BOOL)isDateInRange:(NSDate *)date; -- (void)setSelectedDate:(NSDate *)selectedDate animate:(BOOL)animate forPlaceholder:(BOOL)forPlaceholder; +- (void)selectDate:(NSDate *)date scrollToDate:(BOOL)scrollToDate forPlaceholder:(BOOL)forPlaceholder; - (void)adjustRowHeight; @@ -81,7 +84,7 @@ static BOOL FSCalendarInInterfaceBuilder = NO; @implementation FSCalendar -@dynamic locale; +@dynamic locale, selectedDate; @synthesize scrollDirection = _scrollDirection, firstWeekday = _firstWeekday; #pragma mark - Life Cycle && Initialize @@ -119,6 +122,7 @@ static BOOL FSCalendarInInterfaceBuilder = NO; _scrollDirection = FSCalendarScrollDirectionHorizontal; _firstWeekday = [_calendar firstWeekday]; _scope = FSCalendarScopeMonth; + _selectedDates = [NSMutableArray array]; _rowHeight = -1; @@ -186,12 +190,12 @@ static BOOL FSCalendarInInterfaceBuilder = NO; collectionView.delaysContentTouches = NO; collectionView.canCancelContentTouches = YES; collectionView.scrollsToTop = NO; + collectionView.allowsMultipleSelection = NO; [collectionView registerClass:[FSCalendarCell class] forCellWithReuseIdentifier:@"cell"]; [daysContainer addSubview:collectionView]; self.collectionView = collectionView; self.collectionViewLayout = collectionViewLayout; - UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; view.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.25]; [self addSubview:view]; @@ -272,6 +276,13 @@ static BOOL FSCalendarInInterfaceBuilder = NO; [_appearance adjustTitleIfNecessary]; } + if (_needsReloadingSelectingDates) { + _needsReloadingSelectingDates = NO; + [self.selectedDates enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [self selectDate:obj scrollToDate:NO]; + }]; + } + if (_needsAdjustingMonthPosition) { _needsAdjustingMonthPosition = NO; [self scrollToDate:_currentPage]; @@ -311,7 +322,7 @@ static BOOL FSCalendarInInterfaceBuilder = NO; { FSCalendarInInterfaceBuilder = YES; NSDate *date = [NSDate date]; - self.selectedDate = [NSDate fs_dateWithYear:date.fs_year month:date.fs_month day:_appearance.fakedSelectedDay?:1]; + [self selectDate:[NSDate fs_dateWithYear:date.fs_year month:date.fs_month day:_appearance.fakedSelectedDay?:1]]; } - (CGSize)sizeThatFits:(CGSize)size @@ -375,8 +386,9 @@ static BOOL FSCalendarInInterfaceBuilder = NO; cell.image = [self imageForDate:cell.date]; cell.subtitle = [self subtitleForDate:cell.date]; cell.hasEvent = [self hasEventForDate:cell.date]; - cell.dateIsSelected = [cell.date fs_isEqualToDateForDay:_selectedDate]; + cell.dateIsSelected = [self.selectedDates containsObject:cell.date]; cell.dateIsToday = [cell.date fs_isEqualToDateForDay:_today]; + cell.selected &= cell.dateIsSelected&&!cell.dateIsPlaceholder; switch (_scope) { case FSCalendarScopeMonth: { NSDate *month = [_minimumDate.fs_firstDayOfMonth fs_dateByAddingMonths:indexPath.section].fs_dateByIgnoringTimeComponents; @@ -399,25 +411,24 @@ static BOOL FSCalendarInInterfaceBuilder = NO; { FSCalendarCell *cell = (FSCalendarCell *)[collectionView cellForItemAtIndexPath:indexPath]; if (cell.dateIsPlaceholder) { - [self setSelectedDate:cell.date animate:YES forPlaceholder:YES]; - return; + [self selectDate:cell.date scrollToDate:YES forPlaceholder:YES]; } else { _daysContainer.clipsToBounds = NO; + [cell performSelecting]; - _selectedDate = [self dateForIndexPath:indexPath]; if (!_supressEvent) { - [self didSelectDate:_selectedDate]; + [self didSelectDate:[self dateForIndexPath:indexPath]]; } + + [collectionView.visibleCells enumerateObjectsUsingBlock:^(FSCalendarCell *cell, NSUInteger idx, BOOL *stop) { + if (cell.dateIsPlaceholder) { + cell.dateIsSelected = [self.selectedDates containsObject:cell.date]; + cell.dateIsToday = [cell.date fs_isEqualToDateForDay:_today]; + [cell setNeedsLayout]; + } + }]; + } - // CollectionView选中状态仅仅在‘当月’体现,placeholder需要重新计算'选中'状态 - // There is no stored 'selection' state for placeholder cell, so the 'simulated selection' state needs to be recalculated. - [collectionView.visibleCells enumerateObjectsUsingBlock:^(FSCalendarCell *cell, NSUInteger idx, BOOL *stop) { - if (cell.dateIsPlaceholder) { - cell.dateIsSelected = [cell.date fs_isEqualToDateForDay:_selectedDate]; - cell.dateIsToday = [cell.date fs_isEqualToDateForDay:_today]; - [cell setNeedsLayout]; - } - }]; } @@ -426,14 +437,14 @@ static BOOL FSCalendarInInterfaceBuilder = NO; FSCalendarCell *cell = (FSCalendarCell *)[collectionView cellForItemAtIndexPath:indexPath]; if (cell.dateIsPlaceholder) { if ([self isDateInRange:cell.date]) { - [self setSelectedDate:cell.date animate:YES forPlaceholder:YES]; + [self selectDate:cell.date scrollToDate:YES forPlaceholder:YES]; } else if (![cell.date fs_isEqualToDateForMonth:_currentPage]) { [self scrollToPageForDate:cell.date animated:YES]; } return NO; } if ([collectionView.indexPathsForSelectedItems containsObject:indexPath]) { - [self didSelectDate:_selectedDate]; + [self didSelectDate:self.selectedDate]; return NO; } BOOL shouldSelect = YES; @@ -448,6 +459,7 @@ static BOOL FSCalendarInInterfaceBuilder = NO; FSCalendarCell *cell = (FSCalendarCell *)[collectionView cellForItemAtIndexPath:indexPath]; _daysContainer.clipsToBounds = NO; [cell performDeselecting]; + [_selectedDates removeObject:cell.date]; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView @@ -583,44 +595,6 @@ static BOOL FSCalendarInInterfaceBuilder = NO; } } -- (void)setSelectedDate:(NSDate *)selectedDate -{ - [self setSelectedDate:selectedDate animate:NO]; -} - -- (void)setSelectedDate:(NSDate *)selectedDate animate:(BOOL)animate forPlaceholder:(BOOL)forPlaceholder -{ - if (![self isDateInRange:selectedDate]) { - [NSException raise:@"selectedDate out of range" format:nil]; - } - NSDate *targetDate = selectedDate; - targetDate = [selectedDate fs_daysFrom:_minimumDate] < 0 ? _minimumDate.copy : selectedDate; - targetDate = [selectedDate fs_daysFrom:_maximumDate] > 0 ? _maximumDate.copy : selectedDate; - targetDate = selectedDate.fs_dateByIgnoringTimeComponents; - NSIndexPath *selectedIndexPath = [self indexPathForDate:targetDate]; - - BOOL shouldSelect = YES; - if (forPlaceholder) { - shouldSelect &= !_supressEvent; - shouldSelect &= [self shouldSelectDate:targetDate]; - if (!shouldSelect) return; - } else { - shouldSelect = [self collectionView:_collectionView shouldSelectItemAtIndexPath:selectedIndexPath]; - } - - if (shouldSelect) { - if (_collectionView.indexPathsForSelectedItems.count && _selectedDate) { - NSIndexPath *currentIndexPath = [self indexPathForDate:_selectedDate]; - [_collectionView deselectItemAtIndexPath:currentIndexPath animated:YES]; - [self collectionView:_collectionView didDeselectItemAtIndexPath:currentIndexPath]; - } - [_collectionView selectItemAtIndexPath:selectedIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; - [self collectionView:_collectionView didSelectItemAtIndexPath:selectedIndexPath]; - } - - [self scrollToPageForDate:targetDate animated:animate]; -} - - (void)setToday:(NSDate *)today { if (![self isDateInRange:today]) { @@ -721,6 +695,26 @@ static BOOL FSCalendarInInterfaceBuilder = NO; return 0; } +- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection +{ + _collectionView.allowsMultipleSelection = allowsMultipleSelection; +} + +- (BOOL)allowsMultipleSelection +{ + return _collectionView.allowsMultipleSelection; +} + +- (NSDate *)selectedDate +{ + return self.selectedDates.lastObject; +} + +- (NSArray *)selectedDates +{ + return _selectedDates; +} + #pragma mark - Public - (void)reloadData @@ -746,22 +740,11 @@ static BOOL FSCalendarInInterfaceBuilder = NO; width, height); }]; + _needsReloadingSelectingDates = YES; [_collectionView reloadData]; - if (_selectedDate) { - _supressEvent = YES; - NSIndexPath *selectedIndexPath = [self indexPathForDate:_selectedDate]; - [_collectionView selectItemAtIndexPath:selectedIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; - [self collectionView:_collectionView didSelectItemAtIndexPath:selectedIndexPath]; - _supressEvent = NO; - } [_header reloadData]; } -- (void)setSelectedDate:(NSDate *)selectedDate animate:(BOOL)animate -{ - [self setSelectedDate:selectedDate animate:animate forPlaceholder:NO]; -} - - (void)setScope:(FSCalendarScope)scope animated:(BOOL)animated { if (_scope != scope) { @@ -918,6 +901,51 @@ static BOOL FSCalendarInInterfaceBuilder = NO; } } +- (void)selectDate:(NSDate *)date +{ + [self selectDate:date scrollToDate:YES]; +} + +- (void)selectDate:(NSDate *)date scrollToDate:(BOOL)scrollToDate +{ + [self selectDate:date scrollToDate:scrollToDate forPlaceholder:NO]; +} + +- (void)selectDate:(NSDate *)date scrollToDate:(BOOL)scrollToDate forPlaceholder:(BOOL)forPlaceholder +{ + if (![self isDateInRange:date]) { + [NSException raise:@"selectedDate out of range" format:nil]; + } + NSDate *targetDate = date; + targetDate = [date fs_daysFrom:_minimumDate] < 0 ? _minimumDate.copy : date; + targetDate = [date fs_daysFrom:_maximumDate] > 0 ? _maximumDate.copy : date; + targetDate = date.fs_dateByIgnoringTimeComponents; + NSIndexPath *selectedIndexPath = [self indexPathForDate:targetDate]; + + BOOL shouldSelect = YES; + if (forPlaceholder) { + shouldSelect &= !_supressEvent; + shouldSelect &= [self shouldSelectDate:targetDate]; + if (!shouldSelect) return; + } else { + shouldSelect = [self collectionView:_collectionView shouldSelectItemAtIndexPath:selectedIndexPath]; + } + + if (shouldSelect) { + if (_collectionView.indexPathsForSelectedItems.count && self.selectedDate && !self.allowsMultipleSelection) { + NSIndexPath *currentIndexPath = [self indexPathForDate:self.selectedDate]; + [_collectionView deselectItemAtIndexPath:currentIndexPath animated:YES]; + [self collectionView:_collectionView didDeselectItemAtIndexPath:currentIndexPath]; + } + [_collectionView selectItemAtIndexPath:selectedIndexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + [self collectionView:_collectionView didSelectItemAtIndexPath:selectedIndexPath]; + } + + if (scrollToDate) { + [self scrollToPageForDate:targetDate animated:YES]; + } +} + #pragma mark - Private methods - (void)scrollToDate:(NSDate *)date @@ -1104,6 +1132,12 @@ static BOOL FSCalendarInInterfaceBuilder = NO; - (void)didSelectDate:(NSDate *)date { + if (![_selectedDates containsObject:date]) { + [_selectedDates addObject:date]; + } + [_selectedDates sortUsingComparator:^NSComparisonResult(NSDate *d1, NSDate *d2) { + return [d1 compare:d2] == NSOrderedDescending; + }]; if (_delegate && [_delegate respondsToSelector:@selector(calendar:didSelectDate:)]) { [_delegate calendar:self didSelectDate:date]; } @@ -1195,6 +1229,16 @@ static BOOL FSCalendarInInterfaceBuilder = NO; return (FSCalendarFlow)self.scrollDirection; } +- (void)setSelectedDate:(NSDate *)selectedDate +{ + [self selectDate:selectedDate]; +} + +- (void)setSelectedDate:(NSDate *)selectedDate animate:(BOOL)animate +{ + [self selectDate:selectedDate scrollToDate:animate]; +} + @end diff --git a/FSCalendar/FSCalendarCell.m b/FSCalendar/FSCalendarCell.m index d2115c7..585e3e5 100644 --- a/FSCalendar/FSCalendarCell.m +++ b/FSCalendar/FSCalendarCell.m @@ -89,11 +89,6 @@ [CATransaction setDisableActions:YES]; } -- (BOOL)isSelected -{ - return [super isSelected] || (_dateIsSelected && !_deselecting); -} - #pragma mark - Public - (void)performSelecting @@ -154,7 +149,7 @@ _titleLabel.frame = CGRectMake(0, 0, self.fs_width, floor(self.contentView.fs_height*5.0/6.0)); _subtitleLabel.hidden = YES; } - _backgroundLayer.hidden = !self.selected && !self.dateIsToday; + _backgroundLayer.hidden = !self.selected && !self.dateIsToday && !self.dateIsSelected; _backgroundLayer.path = _appearance.cellStyle == FSCalendarCellStyleCircle ? [UIBezierPath bezierPathWithOvalInRect:_backgroundLayer.bounds].CGPath : [UIBezierPath bezierPathWithRect:_backgroundLayer.bounds].CGPath; @@ -178,7 +173,7 @@ - (UIColor *)colorForCurrentStateInDictionary:(NSDictionary *)dictionary { - if (self.isSelected) { + if (self.isSelected || (self.dateIsSelected && !_deselecting)) { if (self.dateIsToday) { return dictionary[@(FSCalendarCellStateSelected|FSCalendarCellStateToday)] ?: dictionary[@(FSCalendarCellStateSelected)]; } diff --git a/README.md b/README.md index c298523..c7819a2 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Only the methods marked "👍" support IBInspectable / IBDesignable feature. [Ha 4. Finally, you should implement `FSCalendarDataSource` and `FSCalendarDelegate` in ViewController.m -## Code +## Or use code ```objective-c @property (weak , nonatomic) FSCalendar *calendar; @@ -145,6 +145,14 @@ calendar.scope = .Month ![fscalendarscope](https://cloud.githubusercontent.com/assets/5186464/9562222/b0318d40-4e98-11e5-97dc-1694cbd26a74.gif) +### To select more than one date + +```objective-c +_calendar.allowsMultipleSelection = YES; +``` + +![fscalendar-mulipleselection](https://cloud.githubusercontent.com/assets/5186464/9751497/368f55f6-56d8-11e5-9af5-0d09ba13f0eb.png) + ### If you want `FSCalendar` to use `Monday` as the first column (or any other weekday) ```objective-c