diff --git a/Example/FullScreenExampleViewController.m b/Example/FullScreenExampleViewController.m index b42170d..a88ac53 100644 --- a/Example/FullScreenExampleViewController.m +++ b/Example/FullScreenExampleViewController.m @@ -34,6 +34,7 @@ calendar.pagingEnabled = NO; // important calendar.allowsMultipleSelection = YES; calendar.backgroundColor = [UIColor whiteColor]; + calendar.firstWeekday = 2; calendar.appearance.caseOptions = FSCalendarCaseOptionsWeekdayUsesSingleUpperCase|FSCalendarCaseOptionsHeaderUsesUpperCase; [self.view addSubview:calendar]; self.calendar = calendar; diff --git a/Example/StoryboardExampleViewController.m b/Example/StoryboardExampleViewController.m index 5730b17..bcaf8a9 100644 --- a/Example/StoryboardExampleViewController.m +++ b/Example/StoryboardExampleViewController.m @@ -61,6 +61,17 @@ @"2015-10-15", @"2015-10-25"]; + // Uncomment this to test the month->week & week->month transition + /* + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [_calendar setScope:FSCalendarScopeWeek animated:YES]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [_calendar setScope:FSCalendarScopeMonth animated:YES]; + }); + }); + */ + + #if 0 FSCalendarTestSelectDate #endif diff --git a/FSCalendar.podspec b/FSCalendar.podspec index dd290be..462d048 100644 --- a/FSCalendar.podspec +++ b/FSCalendar.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "FSCalendar" - s.version = "1.6.3" + s.version = "1.6.4" s.summary = "Designed to build the best iOS calendar control." s.homepage = "https://github.com/WenchaoIOS/FSCalendar" diff --git a/FSCalendar/FSCalendar.m b/FSCalendar/FSCalendar.m index 131f90a..e3f20ea 100644 --- a/FSCalendar/FSCalendar.m +++ b/FSCalendar/FSCalendar.m @@ -65,7 +65,7 @@ @property (weak , nonatomic) FSCalendarHeader *header; @property (weak , nonatomic) FSCalendarHeaderTouchDeliver *deliver; -@property (strong, nonatomic) NSCalendar *calendar; +@property (weak , nonatomic) NSCalendar *calendar; @property (assign, nonatomic) BOOL ibEditing; @property (assign, nonatomic) BOOL needsAdjustingMonthPosition; @@ -77,8 +77,8 @@ @property (assign, nonatomic) CGFloat preferedHeaderHeight; @property (assign, nonatomic) CGFloat preferedWeekdayHeight; @property (assign, nonatomic) CGFloat preferedRowHeight; +@property (assign, nonatomic) UIInterfaceOrientation interfaceOrientation; -@property (readonly, nonatomic) NSInteger currentSection; @property (readonly, nonatomic) BOOL floatingMode; @property (readonly, nonatomic) NSArray *visibleStickyHeaders; @@ -89,6 +89,7 @@ - (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath; - (NSIndexPath *)indexPathForDate:(NSDate *)date; +- (CGSize)sizeThatFits:(CGSize)size scope:(FSCalendarScope)scope; - (void)scrollToDate:(NSDate *)date; - (void)scrollToDate:(NSDate *)date animated:(BOOL)animated; @@ -171,6 +172,7 @@ _needsAdjustingViewFrame = YES; _needsAdjustingTextSize = YES; _stickyHeaderMapTable = [NSMapTable weakToWeakObjectsMapTable]; + _interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation; UIView *contentView = [[UIView alloc] initWithFrame:CGRectZero]; contentView.backgroundColor = [UIColor clearColor]; @@ -178,6 +180,7 @@ self.contentView = contentView; CAShapeLayer *maskLayer = [CAShapeLayer layer]; + maskLayer.actions = @{@"path":[NSNull null]}; contentView.layer.mask = maskLayer; self.maskLayer = maskLayer; @@ -220,6 +223,7 @@ [self invalidateLayout]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(significantTimeDidChange:) name:UIApplicationSignificantTimeChangeNotification object:nil]; } @@ -265,7 +269,7 @@ _deliver.frame = CGRectMake(_header.fs_left, _header.fs_top, _header.fs_width, headerHeight+weekdayHeight); _deliver.hidden = _header.hidden; - if (_pagingEnabled) { + if (!self.floatingMode) { switch (_scope) { case FSCalendarScopeMonth: { @@ -299,22 +303,21 @@ [_appearance adjustTitleIfNecessary]; } - if (_needsReloadingSelectingDates) { - _needsReloadingSelectingDates = NO; - [_selectedDates enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [self selectDate:obj scrollToDate:NO]; - }]; - } - - if (_needsAdjustingMonthPosition) { - _needsAdjustingMonthPosition = NO; - [self scrollToDate:_pagingEnabled?_currentPage:(_currentPage?:self.selectedDate)]; - } - if (_needsLayoutForWeekMode) { _needsLayoutForWeekMode = NO; _scope = FSCalendarScopeWeek; [self performScopeTransitionFromScope:FSCalendarScopeMonth toScope:FSCalendarScopeWeek animated:NO]; + } else { + if (_needsReloadingSelectingDates) { + _needsReloadingSelectingDates = NO; + [_selectedDates enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [self selectDate:obj scrollToDate:NO]; + }]; + } + if (_needsAdjustingMonthPosition) { + _needsAdjustingMonthPosition = NO; + [self scrollToDate:_pagingEnabled?_currentPage:(_currentPage?:self.selectedDate)]; + } } _supressEvent = NO; @@ -355,14 +358,19 @@ } - (CGSize)sizeThatFits:(CGSize)size +{ + return [self sizeThatFits:size scope:_scope]; +} + +- (CGSize)sizeThatFits:(CGSize)size scope:(FSCalendarScope)scope { CGFloat headerHeight = self.preferedHeaderHeight; CGFloat weekdayHeight = self.preferedWeekdayHeight; CGFloat rowHeight = self.preferedRowHeight; CGFloat paddings = weekdayHeight * 0.2; - + if (!self.floatingMode) { - switch (_scope) { + switch (scope) { case FSCalendarScopeMonth: { CGFloat height = weekdayHeight + headerHeight + 6*rowHeight + paddings; return CGSizeMake(size.width, height); @@ -415,10 +423,9 @@ } } else { NSDate *currentPage = [_minimumDate.fs_firstDayOfMonth fs_dateByAddingMonths:section]; - NSDate *firstDayOfMonth = [NSDate fs_dateWithYear:currentPage.fs_year - month:currentPage.fs_month - day:1]; - NSInteger numberOfRows = (firstDayOfMonth.fs_weekday-_calendar.firstWeekday+currentPage.fs_numberOfDaysInMonth)/7 + ((firstDayOfMonth.fs_weekday-_calendar.firstWeekday+currentPage.fs_numberOfDaysInMonth)%7 !=0 ); + NSDate *firstDayOfMonth = currentPage.fs_firstDayOfMonth; + NSInteger numberOfPlaceholdersForPrev = (firstDayOfMonth.fs_weekday-_calendar.firstWeekday + 7) % 7; + NSInteger numberOfRows = (numberOfPlaceholdersForPrev+firstDayOfMonth.fs_numberOfDaysInMonth)/7 + ((numberOfPlaceholdersForPrev+firstDayOfMonth.fs_numberOfDaysInMonth)%7!=0); return numberOfRows * 7; } return 7; @@ -650,15 +657,7 @@ - (void)orientationDidChange:(NSNotification *)notification { - _needsAdjustingViewFrame = YES; - _needsAdjustingMonthPosition = YES; - _needsAdjustingTextSize = YES; - [_collectionViewLayout invalidateLayout]; // Necessary in Swift. Anyone can tell why? - [_stickyHeaderMapTable.dictionaryRepresentation.allValues setValue:@YES forKey:@"needsAdjustingFrames"]; - _preferedWeekdayHeight = FSCalendarAutomaticDimension; - _preferedRowHeight = FSCalendarAutomaticDimension; - _preferedHeaderHeight = FSCalendarAutomaticDimension; - [self setNeedsLayout]; + self.interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation; } - (void)significantTimeDidChange:(NSNotification *)notification @@ -724,7 +723,7 @@ { if (_firstWeekday != firstWeekday) { _firstWeekday = firstWeekday; - [_calendar setFirstWeekday:firstWeekday]; + _calendar.firstWeekday = firstWeekday; [_collectionView reloadData]; [self invalidateWeekdaySymbols]; } @@ -807,9 +806,12 @@ - (void)setLocale:(NSLocale *)locale { if (![_calendar.locale isEqual:locale]) { - _calendar.locale = locale; - _header.dateFormatter.locale = locale; + // Everything needs to be relocaled + [NSCalendar fs_sharedCalendar].locale = locale; + [NSDateFormatter fs_sharedDateFormatter].locale = locale; + // End [_header reloadData]; + [_stickyHeaderMapTable.dictionaryRepresentation.allValues makeObjectsPerformSelector:@selector(setNeedsLayout)]; [self invalidateWeekdaySymbols]; } } @@ -819,22 +821,6 @@ return _calendar.locale; } -- (NSInteger)currentSection -{ - switch (_scope) { - case FSCalendarScopeMonth: { - return [_currentPage fs_monthsFrom:_minimumDate.fs_firstDayOfMonth]; - } - case FSCalendarScopeWeek: { - return [_currentPage fs_weeksFrom:_minimumDate.fs_firstDayOfWeek]; - } - default: { - break; - } - } - return 0; -} - - (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection { _collectionView.allowsMultipleSelection = allowsMultipleSelection; @@ -876,6 +862,26 @@ } } +- (void)setInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + if (_interfaceOrientation != interfaceOrientation) { + _interfaceOrientation = interfaceOrientation; + + if (interfaceOrientation != UIInterfaceOrientationUnknown) { + _needsAdjustingViewFrame = YES; + _needsAdjustingMonthPosition = YES; + _needsAdjustingTextSize = YES; + [_collectionViewLayout invalidateLayout]; // Necessary in Swift. Anyone can tell why? + [_stickyHeaderMapTable.dictionaryRepresentation.allValues setValue:@YES forKey:@"needsAdjustingFrames"]; + _preferedWeekdayHeight = FSCalendarAutomaticDimension; + _preferedRowHeight = FSCalendarAutomaticDimension; + _preferedHeaderHeight = FSCalendarAutomaticDimension; + [self setNeedsLayout]; + } + + } +} + - (NSDate *)selectedDate { return _selectedDates.lastObject; @@ -1015,7 +1021,8 @@ - (void)performScopeTransitionFromScope:(FSCalendarScope)fromScope toScope:(FSCalendarScope)toScope animated:(BOOL)animated { - NSInteger section = self.currentSection; + BOOL weekToMonth = fromScope == FSCalendarScopeWeek && toScope == FSCalendarScopeMonth; + BOOL monthToWeek = !weekToMonth; void(^completion)(void) = ^{ switch (toScope) { case FSCalendarScopeMonth: { @@ -1032,6 +1039,7 @@ break; } } + [_collectionView reloadData]; [_header reloadData]; _needsAdjustingMonthPosition = YES; @@ -1040,7 +1048,6 @@ [self setNeedsLayout]; }; - BOOL weekToMonth = fromScope == FSCalendarScopeWeek && toScope == FSCalendarScopeMonth; NSInteger rowNumber = -1; if (weekToMonth) { @@ -1074,20 +1081,28 @@ } else { - FSCalendarCell *cell = (FSCalendarCell *)[_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; - _currentPage = cell.date ?: _today; + NSInteger visibleSection = [_currentPage fs_monthsFrom:_minimumDate.fs_firstDayOfMonth]; + NSIndexPath *firstIndexPath = [NSIndexPath indexPathForItem:0 inSection:visibleSection]; + NSDate *firstDateForWeekMode = [self dateForIndexPath:firstIndexPath scope:fromScope]; + _currentPage = firstDateForWeekMode; + } void(^resizeBlock)() = ^{ - CGSize size = [self sizeThatFits:self.frame.size]; - _daysContainer.clipsToBounds = YES; + CGSize size = [self sizeThatFits:self.frame.size scope:toScope]; void(^transitionCompletion)() = ^{ - _daysContainer.clipsToBounds = NO; _maskLayer.path = [UIBezierPath bezierPathWithRect:(CGRect){CGPointZero,size}].CGPath; - if (!weekToMonth) { - completion(); + if (monthToWeek) { + if (animated) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(); + }); + } else { + completion(); + } } + _contentView.clipsToBounds = NO; }; if (animated) { @@ -1130,25 +1145,23 @@ } else { - _needsAdjustingViewFrame = weekToMonth; + _needsAdjustingViewFrame = YES; _bottomBorder.frame = CGRectMake(0, size.height, self.fs_width, 1); + transitionCompletion(); if (_delegate && [_delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)]) { [_delegate calendarCurrentScopeWillChange:self animated:animated]; } - } - }; - if (animated) { - dispatch_async(dispatch_get_main_queue(), ^{ - resizeBlock(); - }); - } else { + _contentView.clipsToBounds = YES; + + dispatch_async(dispatch_get_main_queue(), ^{ resizeBlock(); - } + }); + } - (void)selectDate:(NSDate *)date @@ -1351,9 +1364,9 @@ } } -- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath +- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath scope:(FSCalendarScope)scope { - switch (_scope) { + switch (scope) { case FSCalendarScopeMonth: { NSDate *currentPage = [_minimumDate.fs_firstDayOfMonth fs_dateByAddingMonths:indexPath.section]; NSDate *firstDayOfMonth = currentPage.fs_firstDayOfMonth; @@ -1384,6 +1397,11 @@ return nil; } +- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath +{ + return [self dateForIndexPath:indexPath scope:_scope]; +} + - (NSIndexPath *)indexPathForDate:(NSDate *)date { NSInteger item = 0; diff --git a/FSCalendar/FSCalendarDynamicHeader.h b/FSCalendar/FSCalendarDynamicHeader.h index c9a67e1..b314799 100644 --- a/FSCalendar/FSCalendarDynamicHeader.h +++ b/FSCalendar/FSCalendarDynamicHeader.h @@ -51,7 +51,6 @@ @interface FSCalendarHeader (Dynamic) @property (readonly, nonatomic) UICollectionView *collectionView; -@property (readonly, nonatomic) NSDateFormatter *dateFormatter; @end diff --git a/FSCalendar/FSCalendarHeader.m b/FSCalendar/FSCalendarHeader.m index 9a06ef9..d5b3c18 100644 --- a/FSCalendar/FSCalendarHeader.m +++ b/FSCalendar/FSCalendarHeader.m @@ -15,7 +15,7 @@ @interface FSCalendarHeader () -@property (copy, nonatomic) NSDateFormatter *dateFormatter; +@property (weak, nonatomic) NSDateFormatter *dateFormatter; @property (weak, nonatomic) UICollectionViewFlowLayout *collectionViewFlowLayout; @property (assign, nonatomic) BOOL needsAdjustingMonthPosition; @@ -48,7 +48,7 @@ - (void)initialize { - _dateFormatter = [[NSDateFormatter alloc] init]; + _dateFormatter = [NSDateFormatter fs_sharedDateFormatter]; _scrollDirection = UICollectionViewScrollDirectionHorizontal; _scrollEnabled = YES; @@ -167,7 +167,7 @@ if ((indexPath.item == 0 || indexPath.item == [collectionView numberOfItemsInSection:0] - 1)) { text = nil; } else { - NSDate *date = [self.calendar.minimumDate.fs_firstDayOfWeek fs_dateByAddingWeeks:indexPath.item - 1].fs_dateByIgnoringTimeComponents; + NSDate *date = [self.calendar.minimumDate.fs_middleOfWeek fs_dateByAddingWeeks:indexPath.item - 1].fs_dateByIgnoringTimeComponents; text = [_dateFormatter stringFromDate:date]; } break; diff --git a/FSCalendar/FSCalendarStickyHeader.h b/FSCalendar/FSCalendarStickyHeader.h index d329e2f..4c7531b 100644 --- a/FSCalendar/FSCalendarStickyHeader.h +++ b/FSCalendar/FSCalendarStickyHeader.h @@ -15,8 +15,8 @@ @property (weak, nonatomic) FSCalendar *calendar; @property (weak, nonatomic) FSCalendarAppearance *appearance; @property (weak, nonatomic) UILabel *titleLabel; -@property (strong, nonatomic) NSArray *weekdayLabels; +@property (strong, nonatomic) NSArray *weekdayLabels; @property (strong, nonatomic) NSDate *month; @end \ No newline at end of file diff --git a/FSCalendar/FSCalendarStickyHeader.m b/FSCalendar/FSCalendarStickyHeader.m index 31960a4..2eb7eb8 100644 --- a/FSCalendar/FSCalendarStickyHeader.m +++ b/FSCalendar/FSCalendarStickyHeader.m @@ -18,7 +18,7 @@ @property (weak, nonatomic) UIView *contentView; @property (weak, nonatomic) UIView *separator; -@property (strong, nonatomic) NSDateFormatter *dateFormatter; +@property (weak, nonatomic) NSDateFormatter *dateFormatter; @property (assign, nonatomic) BOOL needsReloadingAppearance; @property (assign, nonatomic) BOOL needsAdjustingFrames; @@ -35,9 +35,9 @@ self = [super initWithFrame:frame]; if (self) { - self.dateFormatter = [[NSDateFormatter alloc] init]; - self.needsReloadingAppearance = YES; - self.needsAdjustingFrames = YES; + _dateFormatter = [NSDateFormatter fs_sharedDateFormatter]; + _needsReloadingAppearance = YES; + _needsAdjustingFrames = YES; UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; view.backgroundColor = [UIColor clearColor]; diff --git a/FSCalendar/Info.plist b/FSCalendar/Info.plist index 148a1d1..f1ed6f9 100644 --- a/FSCalendar/Info.plist +++ b/FSCalendar/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.6.1 + 1.6.4 CFBundleSignature ???? CFBundleVersion diff --git a/FSCalendar/NSDate+FSExtension.h b/FSCalendar/NSDate+FSExtension.h index 74568d0..090e4ca 100644 --- a/FSCalendar/NSDate+FSExtension.h +++ b/FSCalendar/NSDate+FSExtension.h @@ -23,6 +23,7 @@ @property (readonly, nonatomic) NSDate *fs_firstDayOfMonth; @property (readonly, nonatomic) NSDate *fs_lastDayOfMonth; @property (readonly, nonatomic) NSDate *fs_firstDayOfWeek; +@property (readonly, nonatomic) NSDate *fs_middleOfWeek; @property (readonly, nonatomic) NSDate *fs_tomorrow; @property (readonly, nonatomic) NSDate *fs_yesterday; @property (readonly, nonatomic) NSInteger fs_numberOfDaysInMonth; diff --git a/FSCalendar/NSDate+FSExtension.m b/FSCalendar/NSDate+FSExtension.m index 8740785..7a46413 100644 --- a/FSCalendar/NSDate+FSExtension.m +++ b/FSCalendar/NSDate+FSExtension.m @@ -108,6 +108,19 @@ return beginningOfWeek; } +- (NSDate *)fs_middleOfWeek +{ + NSCalendar *calendar = [NSCalendar fs_sharedCalendar]; + NSDateComponents *weekdayComponents = [calendar components:NSCalendarUnitWeekday fromDate:self]; + NSDateComponents *componentsToSubtract = [NSDateComponents fs_sharedDateComponents]; + componentsToSubtract.day = - (weekdayComponents.weekday - calendar.firstWeekday) + 3; + NSDate *middleOfWeek = [calendar dateByAddingComponents:componentsToSubtract toDate:self options:0]; + NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:middleOfWeek]; + middleOfWeek = [calendar dateFromComponents:components]; + componentsToSubtract.day = NSIntegerMax; + return middleOfWeek; +} + - (NSDate *)fs_tomorrow { NSCalendar *calendar = [NSCalendar fs_sharedCalendar]; diff --git a/SwiftExample/SwiftExample/ViewController.swift b/SwiftExample/SwiftExample/ViewController.swift index 096f5b9..812eec9 100644 --- a/SwiftExample/SwiftExample/ViewController.swift +++ b/SwiftExample/SwiftExample/ViewController.swift @@ -17,16 +17,16 @@ class ViewController: UIViewController, FSCalendarDataSource, FSCalendarDelegate super.viewDidLoad() calendar.scrollDirection = .Vertical calendar.appearance.caseOptions = [.HeaderUsesUpperCase,.WeekdayUsesUpperCase] - calendar.selectDate(NSDate()) +// calendar.allowsMultipleSelection = true + // Uncomment this to test month->week and week->month transition /* - calendar.allowsMultipleSelection = true - var dateArray = ["20160101", "20151115", "20151211", "20151201", "20151107", "20160105"] - for (var i = 0 ; i < dateArray.count; i++) { - let dateString = dateArray[i] as NSString - let date = dateString.fs_dateWithFormat("yyyyMMdd"); - calendar.selectDate(date) + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(1.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in + self.calendar.setScope(.Week, animated: true) + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(1.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in + self.calendar.setScope(.Month, animated: true) + } } */ @@ -56,6 +56,7 @@ class ViewController: UIViewController, FSCalendarDataSource, FSCalendarDelegate func calendarCurrentScopeWillChange(calendar: FSCalendar!, animated: Bool) { calendarHeightConstraint.constant = calendar.sizeThatFits(CGSizeZero).height + view.layoutIfNeeded() } func calendar(calendar: FSCalendar!, imageForDate date: NSDate!) -> UIImage! {