Add 'adjustsBoundRectWhenMonthChanges' property to FSCalendar.

This commit is contained in:
WenchaoD 2018-10-05 16:18:03 +08:00
parent eebc82594d
commit 1be7ecbeb2
9 changed files with 212 additions and 550 deletions

View File

@ -49,7 +49,6 @@ NS_ASSUME_NONNULL_END
if ([[UIDevice currentDevice].model hasPrefix:@"iPad"]) {
self.calendarHeightConstraint.constant = 400;
}
[self.calendar selectDate:[NSDate date] scrollToDate:YES];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self.calendar action:@selector(handleScopeGesture:)];
@ -63,7 +62,7 @@ NS_ASSUME_NONNULL_END
[self.tableView.panGestureRecognizer requireGestureRecognizerToFail:panGesture];
[self.calendar addObserver:self forKeyPath:@"scope" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:_KVOContext];
self.calendar.placeholderType = FSCalendarPlaceholderTypeNone;
self.calendar.scope = FSCalendarScopeWeek;
// For UITest

View File

@ -62,8 +62,8 @@ NS_ASSUME_NONNULL_END
calendar.backgroundColor = [UIColor whiteColor];
calendar.dataSource = self;
calendar.delegate = self;
// calendar.placeholderType = FSCalendarPlaceholderTypeFillHeadTail;
calendar.placeholderType = FSCalendarPlaceholderTypeNone;
calendar.adjustsBoundingRectWhenChangingMonths = YES;
calendar.currentPage = [self.dateFormatter dateFromString:@"2016-06-01"];
calendar.firstWeekday = 2;
calendar.scrollDirection = FSCalendarScrollDirectionVertical;

View File

@ -153,12 +153,6 @@
NSLog(@"did change to page %@",[self.dateFormatter1 stringFromDate:calendar.currentPage]);
}
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated
{
_calendarHeightConstraint.constant = CGRectGetHeight(bounds);
[self.view layoutIfNeeded];
}
- (CGPoint)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance titleOffsetForDate:(NSDate *)date
{
if ([self calendar:calendar subtitleForDate:date]) {

View File

@ -366,6 +366,11 @@ IB_DESIGNABLE
*/
@property (assign, nonatomic) IBInspectable BOOL allowsMultipleSelection;
/**
A Boolean value that determines whether the bounding rect changes when the displayed month of the calendar is changed.
*/
@property (assign, nonatomic) IBInspectable BOOL adjustsBoundingRectWhenChangingMonths;
/**
A Boolean value that determines whether paging is enabled for the calendar.
*/

View File

@ -67,7 +67,6 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
@property (weak , nonatomic) FSCalendarHeaderTouchDeliver *deliver;
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
@property (assign, nonatomic) BOOL needsLayoutForWeekMode;
@property (assign, nonatomic) BOOL needsRequestingBoundingDates;
@property (assign, nonatomic) CGFloat preferredHeaderHeight;
@property (assign, nonatomic) CGFloat preferredWeekdayHeight;
@ -191,6 +190,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
UIView *contentView = [[UIView alloc] initWithFrame:CGRectZero];
contentView.backgroundColor = [UIColor clearColor];
contentView.clipsToBounds = YES;
[self addSubview:contentView];
self.contentView = contentView;
@ -330,10 +330,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
_daysContainer.frame = CGRectMake(0, headerHeight+weekdayHeight, self.fs_width, contentHeight);
_collectionView.frame = CGRectMake(0, 0, _daysContainer.fs_width, contentHeight);
if (needsAdjustingBoundingRect) {
self.transitionCoordinator.state = FSCalendarTransitionStateChanging;
CGRect boundingRect = (CGRect){CGPointZero,[self sizeThatFits:self.frame.size]};
[self.delegateProxy calendar:self boundingRectWillChange:boundingRect animated:NO];
self.transitionCoordinator.state = FSCalendarTransitionStateIdle;
[self.transitionCoordinator performBoundingRectTransitionFromMonth:nil toMonth:self.currentPage duration:0];
}
break;
}
@ -356,11 +353,6 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
_bottomBorder.frame = CGRectMake(0, self.fs_height, self.fs_width, 1);
}
if (_needsLayoutForWeekMode) {
_needsLayoutForWeekMode = NO;
[self.transitionCoordinator performScopeTransitionFromScope:FSCalendarScopeMonth toScope:FSCalendarScopeWeek animated:NO];
}
}
#if TARGET_INTERFACE_BUILDER
@ -376,17 +368,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
- (CGSize)sizeThatFits:(CGSize)size
{
switch (self.transitionCoordinator.transition) {
case FSCalendarTransitionNone:
return [self sizeThatFits:size scope:_scope];
case FSCalendarTransitionWeekToMonth:
if (self.transitionCoordinator.state == FSCalendarTransitionStateChanging) {
return [self sizeThatFits:size scope:FSCalendarScopeMonth];
}
case FSCalendarTransitionMonthToWeek:
break;
}
return [self sizeThatFits:size scope:FSCalendarScopeWeek];
return [self sizeThatFits:size scope:self.transitionCoordinator.representingScope];
}
- (CGSize)sizeThatFits:(CGSize)size scope:(FSCalendarScope)scope
@ -927,7 +909,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
if (_preferredWeekdayHeight == FSCalendarAutomaticDimension) {
if (!self.floatingMode) {
CGFloat DIYider = FSCalendarStandardMonthlyPageHeight;
CGFloat contentHeight = self.transitionCoordinator.cachedMonthSize.height*(1-_showsScopeHandle*0.08);
CGFloat contentHeight = self.transitionCoordinator.cachedMonthSize.height;
_preferredHeaderHeight = (FSCalendarStandardHeaderHeight/DIYider)*contentHeight;
_preferredHeaderHeight -= (_preferredHeaderHeight-FSCalendarStandardHeaderHeight)*0.5;
} else {
@ -945,7 +927,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
if (_preferredWeekdayHeight == FSCalendarAutomaticDimension) {
if (!self.floatingMode) {
CGFloat DIYider = FSCalendarStandardMonthlyPageHeight;
CGFloat contentHeight = self.transitionCoordinator.cachedMonthSize.height*(1-_showsScopeHandle*0.08);
CGFloat contentHeight = self.transitionCoordinator.cachedMonthSize.height;
_preferredWeekdayHeight = (FSCalendarStandardWeekdayHeight/DIYider)*contentHeight;
} else {
_preferredWeekdayHeight = FSCalendarStandardWeekdayHeight*MAX(1, FSCalendarDeviceIsIPad*1.5);
@ -977,14 +959,6 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
return _scope == FSCalendarScopeMonth && _scrollEnabled && !_pagingEnabled;
}
- (void)setShowsScopeHandle:(BOOL)showsScopeHandle
{
if (_showsScopeHandle != showsScopeHandle) {
_showsScopeHandle = showsScopeHandle;
[self invalidateLayout];
}
}
- (UIPanGestureRecognizer *)scopeGesture
{
if (!_scopeGesture) {
@ -1050,20 +1024,14 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
if (self.floatingMode) return;
if (self.transitionCoordinator.state != FSCalendarTransitionStateIdle) return;
FSCalendarScope prevScope = _scope;
[self willChangeValueForKey:@"scope"];
_scope = scope;
[self didChangeValueForKey:@"scope"];
if (prevScope == scope) return;
if (!self.hasValidateVisibleLayout && prevScope == FSCalendarScopeMonth && scope == FSCalendarScopeWeek) {
_needsLayoutForWeekMode = YES;
BOOL hasLayout = self.hasValidateVisibleLayout;
if (!hasLayout) {
[self setNeedsLayout];
} else if (self.transitionCoordinator.state == FSCalendarTransitionStateIdle) {
[self.transitionCoordinator performScopeTransitionFromScope:prevScope toScope:scope animated:animated];
[self.collectionViewLayout invalidateLayout];
[self layoutIfNeeded];
}
BOOL needsAnimate = hasLayout && animated;
[self.transitionCoordinator performScopeTransitionFromScope:self.scope toScope:scope animated:needsAnimate];
}
- (void)setPlaceholderType:(FSCalendarPlaceholderType)placeholderType

View File

@ -198,16 +198,12 @@
- (NSInteger)numberOfSections
{
if (self.calendar.transitionCoordinator.transition == FSCalendarTransitionWeekToMonth) {
return self.numberOfMonths;
} else {
switch (self.calendar.transitionCoordinator.representingScope) {
case FSCalendarScopeMonth: {
return self.numberOfMonths;
}
case FSCalendarScopeWeek: {
return self.numberOfWeeks;
}
switch (self.calendar.transitionCoordinator.representingScope) {
case FSCalendarScopeMonth: {
return self.numberOfMonths;
}
case FSCalendarScopeWeek: {
return self.numberOfWeeks;
}
}
}

View File

@ -41,6 +41,7 @@
@property (strong, nonatomic) NSMutableDictionary<NSIndexPath *, UICollectionViewLayoutAttributes *> *rowSeparatorAttributes;
- (void)didReceiveNotifications:(NSNotification *)notification;
- (CGFloat)calculateRowOffset:(NSInteger)row totalRows:(NSInteger)totalRows;
@end
@ -404,21 +405,26 @@
FSCalendarCoordinate coordinate = [self.calendar.calculator coordinateForIndexPath:indexPath];
NSInteger column = coordinate.column;
NSInteger row = coordinate.row;
NSInteger numberOfRows = [self.calendar.calculator numberOfRowsInSection:indexPath.section];
UICollectionViewLayoutAttributes *attributes = self.itemAttributes[indexPath];
if (!attributes) {
attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGRect frame = ({
CGFloat width = self.widths[column];
CGFloat height = self.heights[row];
CGFloat x, y;
switch (self.scrollDirection) {
case UICollectionViewScrollDirectionHorizontal: {
x = self.lefts[column] + indexPath.section * self.collectionView.fs_width;
y = self.tops[row];
y = [self calculateRowOffset:row totalRows:numberOfRows];
break;
}
case UICollectionViewScrollDirectionVertical: {
x = self.lefts[column];
if (!self.calendar.floatingMode) {
y = self.tops[row] + indexPath.section * self.collectionView.fs_height;
CGFloat sectionTop = indexPath.section * self.collectionView.fs_height;
CGFloat rowOffset = [self calculateRowOffset:row totalRows:numberOfRows];
y = sectionTop + rowOffset;
} else {
y = self.sectionTops[indexPath.section] + self.headerReferenceSize.height + self.tops[row];
}
@ -427,8 +433,6 @@
default:
break;
}
CGFloat width = self.widths[column];
CGFloat height = self.heights[row];
CGRect frame = CGRectMake(x, y, width, height);
frame;
});
@ -466,15 +470,16 @@
attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:kFSCalendarSeparatorInterRows withIndexPath:indexPath];
CGFloat x, y;
if (!self.calendar.floatingMode) {
CGFloat rowOffset = [self calculateRowOffset:coordinate.row totalRows:[self.calendar.calculator numberOfRowsInSection:indexPath.section]] + self.heights[coordinate.row];
switch (self.scrollDirection) {
case UICollectionViewScrollDirectionHorizontal: {
x = self.lefts[coordinate.column] + indexPath.section * self.collectionView.fs_width;
y = self.tops[coordinate.row]+self.heights[coordinate.row];
y = rowOffset;
break;
}
case UICollectionViewScrollDirectionVertical: {
x = 0;
y = self.tops[coordinate.row]+self.heights[coordinate.row] + indexPath.section * self.collectionView.fs_height;
y = indexPath.section * self.collectionView.fs_height + rowOffset;
break;
}
default:
@ -526,6 +531,25 @@
#pragma mark - Private functions
- (CGFloat)calculateRowOffset:(NSInteger)row totalRows:(NSInteger)totalRows
{
if (self.calendar.adjustsBoundingRectWhenChangingMonths) {
return self.tops[row];
}
CGFloat height = self.heights[row];
switch (totalRows) {
case 4:
case 5: {
CGFloat contentHeight = self.collectionView.fs_height - self.sectionInsets.top - self.sectionInsets.bottom;
CGFloat rowSpan = contentHeight/totalRows;
return (row + 0.5) * rowSpan - height * 0.5 + self.sectionInsets.top;
}
case 6:
default:
return self.tops[row];
}
}
- (NSInteger)searchStartSection:(CGRect)rect :(NSInteger)left :(NSInteger)right
{
NSInteger mid = left + (right-left)/2;

View File

@ -10,11 +10,6 @@
#import "FSCalendarCollectionView.h"
#import "FSCalendarCollectionViewLayout.h"
typedef NS_ENUM(NSUInteger, FSCalendarTransition) {
FSCalendarTransitionNone,
FSCalendarTransitionMonthToWeek,
FSCalendarTransitionWeekToMonth
};
typedef NS_ENUM(NSUInteger, FSCalendarTransitionState) {
FSCalendarTransitionStateIdle,
FSCalendarTransitionStateChanging,
@ -23,11 +18,6 @@ typedef NS_ENUM(NSUInteger, FSCalendarTransitionState) {
@interface FSCalendarTransitionCoordinator : NSObject <UIGestureRecognizerDelegate>
@property (weak, nonatomic) FSCalendar *calendar;
@property (weak, nonatomic) FSCalendarCollectionView *collectionView;
@property (weak, nonatomic) FSCalendarCollectionViewLayout *collectionViewLayout;
@property (assign, nonatomic) FSCalendarTransition transition;
@property (assign, nonatomic) FSCalendarTransitionState state;
@property (assign, nonatomic) CGSize cachedMonthSize;
@ -38,6 +28,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarTransitionState) {
- (void)performScopeTransitionFromScope:(FSCalendarScope)fromScope toScope:(FSCalendarScope)toScope animated:(BOOL)animated;
- (void)performBoundingRectTransitionFromMonth:(NSDate *)fromMonth toMonth:(NSDate *)toMonth duration:(CGFloat)duration;
- (CGRect)boundingRectForScope:(FSCalendarScope)scope page:(NSDate *)page;
- (void)handleScopeGesture:(id)sender;
@ -50,9 +41,11 @@ typedef NS_ENUM(NSUInteger, FSCalendarTransitionState) {
@property (assign, nonatomic) CGRect targetBounds;
@property (strong, nonatomic) NSDate *sourcePage;
@property (strong, nonatomic) NSDate *targetPage;
@property (assign, nonatomic) NSInteger focusedRowNumber;
@property (assign, nonatomic) NSDate *focusedDate;
@property (strong, nonatomic) NSDate *firstDayOfMonth;
@property (assign, nonatomic) NSInteger focusedRow;
@property (strong, nonatomic) NSDate *focusedDate;
@property (assign, nonatomic) FSCalendarScope targetScope;
- (void)revert;
@end

View File

@ -9,20 +9,19 @@
#import "FSCalendarTransitionCoordinator.h"
#import "FSCalendarExtensions.h"
#import "FSCalendarDynamicHeader.h"
#import <objc/runtime.h>
@interface FSCalendarTransitionCoordinator ()
@property (readonly, nonatomic) FSCalendarTransitionAttributes *transitionAttributes;
@property (strong , nonatomic) FSCalendarTransitionAttributes *pendingAttributes;
@property (assign , nonatomic) CGFloat lastTranslation;
@property (weak, nonatomic) FSCalendarCollectionView *collectionView;
@property (weak, nonatomic) FSCalendarCollectionViewLayout *collectionViewLayout;
@property (weak, nonatomic) FSCalendar *calendar;
@property (strong, nonatomic) FSCalendarTransitionAttributes *transitionAttributes;
- (FSCalendarTransitionAttributes *)createTransitionAttributesTargetingScope:(FSCalendarScope)targetScope;
- (void)performTransitionCompletionAnimated:(BOOL)animated;
- (void)performTransitionCompletion:(FSCalendarTransition)transition animated:(BOOL)animated;
- (void)performAlphaAnimationFrom:(CGFloat)fromAlpha to:(CGFloat)toAlpha duration:(CGFloat)duration exception:(NSInteger)exception completion:(void(^)(void))completion;
- (void)performForwardTransition:(FSCalendarTransition)transition fromProgress:(CGFloat)progress;
- (void)performBackwardTransition:(FSCalendarTransition)transition fromProgress:(CGFloat)progress;
- (void)performAlphaAnimationWithProgress:(CGFloat)progress;
- (void)performPathAnimationWithProgress:(CGFloat)progress;
@ -30,8 +29,6 @@
- (void)scopeTransitionDidUpdate:(UIPanGestureRecognizer *)panGesture;
- (void)scopeTransitionDidEnd:(UIPanGestureRecognizer *)panGesture;
- (CGRect)boundingRectForScope:(FSCalendarScope)scope page:(NSDate *)page;
- (void)boundingRectWillChange:(CGRect)targetBounds animated:(BOOL)animated;
@end
@ -115,34 +112,18 @@
if (self.state != FSCalendarTransitionStateIdle) return;
CGPoint velocity = [panGesture velocityInView:panGesture.view];
switch (self.calendar.scope) {
case FSCalendarScopeMonth: {
if (velocity.y < 0) {
self.state = FSCalendarTransitionStateChanging;
self.transition = FSCalendarTransitionMonthToWeek;
}
break;
}
case FSCalendarScopeWeek: {
if (velocity.y > 0) {
self.state = FSCalendarTransitionStateChanging;
self.transition = FSCalendarTransitionWeekToMonth;
}
break;
}
default:
break;
if (self.calendar.scope == FSCalendarScopeMonth && velocity.y >= 0) {
return;
}
if (self.state != FSCalendarTransitionStateChanging) return;
if (self.calendar.scope == FSCalendarScopeWeek && velocity.y <= 0) {
return;
}
self.state = FSCalendarTransitionStateChanging;
self.pendingAttributes = self.transitionAttributes;
self.lastTranslation = [panGesture translationInView:panGesture.view].y;
self.transitionAttributes = [self createTransitionAttributesTargetingScope:1-self.calendar.scope];
if (self.transition == FSCalendarTransitionWeekToMonth) {
[self.calendar fs_setVariable:self.pendingAttributes.targetPage forKey:@"_currentPage"];
[self prelayoutForWeekToMonthTransition];
self.collectionView.fs_top = -self.pendingAttributes.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height;
if (self.transitionAttributes.targetScope == FSCalendarScopeMonth) {
[self prepareWeekToMonthTransition];
}
}
@ -150,40 +131,16 @@
{
if (self.state != FSCalendarTransitionStateChanging) return;
CGFloat translation = [panGesture translationInView:panGesture.view].y;
[CATransaction begin];
[CATransaction setDisableActions:YES];
switch (self.transition) {
case FSCalendarTransitionMonthToWeek: {
CGFloat progress = ({
CGFloat minTranslation = CGRectGetHeight(self.pendingAttributes.targetBounds) - CGRectGetHeight(self.pendingAttributes.sourceBounds);
translation = MAX(minTranslation, translation);
translation = MIN(0, translation);
CGFloat progress = translation/minTranslation;
progress;
});
[self performAlphaAnimationWithProgress:progress];
[self performPathAnimationWithProgress:progress];
break;
}
case FSCalendarTransitionWeekToMonth: {
CGFloat progress = ({
CGFloat maxTranslation = CGRectGetHeight(self.pendingAttributes.targetBounds) - CGRectGetHeight(self.pendingAttributes.sourceBounds);
translation = MIN(maxTranslation, translation);
translation = MAX(0, translation);
CGFloat progress = translation/maxTranslation;
progress;
});
[self performAlphaAnimationWithProgress:progress];
[self performPathAnimationWithProgress:progress];
break;
}
default:
break;
}
[CATransaction commit];
self.lastTranslation = translation;
CGFloat translation = ABS([panGesture translationInView:panGesture.view].y);
CGFloat progress = ({
CGFloat maxTranslation = ABS(CGRectGetHeight(self.transitionAttributes.targetBounds) - CGRectGetHeight(self.transitionAttributes.sourceBounds));
translation = MIN(maxTranslation, translation);
translation = MAX(0, translation);
CGFloat progress = translation/maxTranslation;
progress;
});
[self performAlphaAnimationWithProgress:progress];
[self performPathAnimationWithProgress:progress];
}
- (void)scopeTransitionDidEnd:(UIPanGestureRecognizer *)panGesture
@ -195,148 +152,48 @@
CGFloat translation = [panGesture translationInView:panGesture.view].y;
CGFloat velocity = [panGesture velocityInView:panGesture.view].y;
switch (self.transition) {
case FSCalendarTransitionMonthToWeek: {
CGFloat progress = ({
CGFloat minTranslation = CGRectGetHeight(self.pendingAttributes.targetBounds) - CGRectGetHeight(self.pendingAttributes.sourceBounds);
translation = MAX(minTranslation, translation);
translation = MIN(0, translation);
CGFloat progress = translation/minTranslation;
progress;
});
if (velocity >= 0) {
[self performBackwardTransition:self.transition fromProgress:progress];
} else {
[self performForwardTransition:self.transition fromProgress:progress];
}
break;
}
case FSCalendarTransitionWeekToMonth: {
CGFloat progress = ({
CGFloat maxTranslation = CGRectGetHeight(self.pendingAttributes.targetBounds) - CGRectGetHeight(self.pendingAttributes.sourceBounds);
translation = MAX(0, translation);
translation = MIN(maxTranslation, translation);
CGFloat progress = translation/maxTranslation;
progress;
});
if (velocity >= 0) {
[self performForwardTransition:self.transition fromProgress:progress];
} else {
[self performBackwardTransition:self.transition fromProgress:progress];
}
break;
}
default:
break;
CGFloat progress = ({
CGFloat maxTranslation = CGRectGetHeight(self.transitionAttributes.targetBounds) - CGRectGetHeight(self.transitionAttributes.sourceBounds);
translation = MAX(0, translation);
translation = MIN(maxTranslation, translation);
CGFloat progress = translation/maxTranslation;
progress;
});
if (velocity * translation < 0) {
[self.transitionAttributes revert];
}
[self performTransition:self.transitionAttributes.targetScope fromProgress:progress toProgress:1.0 animated:YES];
}
#pragma mark - Public methods
- (void)performScopeTransitionFromScope:(FSCalendarScope)fromScope toScope:(FSCalendarScope)toScope animated:(BOOL)animated
{
if (fromScope == toScope) return;
self.transition = ({
FSCalendarTransition transition = FSCalendarTransitionNone;
if (fromScope == FSCalendarScopeMonth && toScope == FSCalendarScopeWeek) {
transition = FSCalendarTransitionMonthToWeek;
} else if (fromScope == FSCalendarScopeWeek && toScope == FSCalendarScopeMonth) {
transition = FSCalendarTransitionWeekToMonth;
}
transition;
});
if (fromScope == toScope) {
[self.calendar willChangeValueForKey:@"scope"];
[self.calendar fs_setUnsignedIntegerVariable:toScope forKey:@"_scope"];
[self.calendar didChangeValueForKey:@"scope"];
return;
}
// Start transition
self.state = FSCalendarTransitionStateFinishing;
FSCalendarTransitionAttributes *attr = self.transitionAttributes;
self.pendingAttributes = attr;
switch (self.transition) {
case FSCalendarTransitionMonthToWeek: {
[self.calendar fs_setVariable:attr.targetPage forKey:@"_currentPage"];
self.calendar.contentView.clipsToBounds = YES;
if (animated) {
CGFloat duration = 0.3;
[self performAlphaAnimationFrom:1 to:0 duration:0.22 exception:attr.focusedRowNumber completion:^{
[self performTransitionCompletionAnimated:animated];
}];
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationsEnabled:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:duration];
self.collectionView.fs_top = -attr.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height;
[self boundingRectWillChange:attr.targetBounds animated:animated];
[UIView commitAnimations];
}
} else {
[self performTransitionCompletionAnimated:animated];
[self boundingRectWillChange:attr.targetBounds animated:animated];
}
break;
}
case FSCalendarTransitionWeekToMonth: {
[self.calendar fs_setVariable:attr.targetPage forKey:@"_currentPage"];
[self prelayoutForWeekToMonthTransition];
if (animated) {
CGFloat duration = 0.3;
[self performAlphaAnimationFrom:0 to:1 duration:duration exception:attr.focusedRowNumber completion:^{
[self performTransitionCompletionAnimated:animated];
}];
[CATransaction begin];
[CATransaction setDisableActions:NO];
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
self.collectionView.fs_top = -attr.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationsEnabled:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:duration];
self.collectionView.fs_top = 0;
[self boundingRectWillChange:attr.targetBounds animated:animated];
[UIView commitAnimations];
}
[CATransaction commit];
} else {
[self performTransitionCompletionAnimated:animated];
[self boundingRectWillChange:attr.targetBounds animated:animated];
}
break;
}
default:
break;
FSCalendarTransitionAttributes *attr = [self createTransitionAttributesTargetingScope:toScope];
self.transitionAttributes = attr;
if (toScope == FSCalendarScopeMonth) {
[self prepareWeekToMonthTransition];
}
[self performTransition:self.transitionAttributes.targetScope fromProgress:0 toProgress:1 animated:animated];
}
- (void)performBoundingRectTransitionFromMonth:(NSDate *)fromMonth toMonth:(NSDate *)toMonth duration:(CGFloat)duration
{
if (!self.calendar.adjustsBoundingRectWhenChangingMonths) return;
if (self.calendar.scope != FSCalendarScopeMonth) return;
NSInteger lastRowCount = [self.calendar.calculator numberOfRowsInMonth:fromMonth];
NSInteger currentRowCount = [self.calendar.calculator numberOfRowsInMonth:toMonth];
if (lastRowCount != currentRowCount) {
CGFloat animationDuration = duration;
CGRect bounds = (CGRect){CGPointZero,[self.calendar sizeThatFits:self.calendar.frame.size scope:FSCalendarScopeMonth]};
CGRect bounds = [self boundingRectForScope:FSCalendarScopeMonth page:toMonth];
self.state = FSCalendarTransitionStateChanging;
void (^completion)(BOOL) = ^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(MAX(0, duration-animationDuration) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@ -362,16 +219,8 @@
- (void)performTransitionCompletionAnimated:(BOOL)animated
{
[self performTransitionCompletion:self.transition animated:animated];
}
- (void)performTransitionCompletion:(FSCalendarTransition)transition animated:(BOOL)animated
{
switch (transition) {
case FSCalendarTransitionMonthToWeek: {
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(UICollectionViewCell *obj, NSUInteger idx, BOOL * stop) {
obj.contentView.layer.opacity = 1;
}];
switch (self.transitionAttributes.targetScope) {
case FSCalendarScopeWeek: {
self.collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.calendar.calendarHeaderView.scrollDirection = self.collectionViewLayout.scrollDirection;
self.calendar.needsAdjustingViewFrame = YES;
@ -379,120 +228,56 @@
[self.calendar.calendarHeaderView reloadData];
break;
}
case FSCalendarTransitionWeekToMonth: {
case FSCalendarScopeMonth: {
self.calendar.needsAdjustingViewFrame = YES;
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(UICollectionViewCell *obj, NSUInteger idx, BOOL * stop) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
obj.contentView.layer.opacity = 1;
[CATransaction commit];
[obj.contentView.layer removeAnimationForKey:@"opacity"];
}];
break;
}
default:
break;
}
self.state = FSCalendarTransitionStateIdle;
self.transition = FSCalendarTransitionNone;
self.calendar.contentView.clipsToBounds = NO;
self.pendingAttributes = nil;
self.transitionAttributes = nil;
[self.calendar setNeedsLayout];
[self.calendar layoutIfNeeded];
}
- (FSCalendarTransitionAttributes *)transitionAttributes
- (FSCalendarTransitionAttributes *)createTransitionAttributesTargetingScope:(FSCalendarScope)targetScope
{
FSCalendarTransitionAttributes *attributes = [[FSCalendarTransitionAttributes alloc] init];
attributes.sourceBounds = self.calendar.bounds;
attributes.sourcePage = self.calendar.currentPage;
switch (self.transition) {
case FSCalendarTransitionMonthToWeek: {
NSDate *focusedDate = ({
NSArray<NSDate *> *candidates = ({
NSMutableArray *dates = self.calendar.selectedDates.reverseObjectEnumerator.allObjects.mutableCopy;
if (self.calendar.today) {
[dates addObject:self.calendar.today];
}
if (self.calendar.currentPage) {
[dates addObject:self.calendar.currentPage];
}
dates.copy;
});
NSArray<NSDate *> *visibleCandidates = [candidates filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDate * _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:evaluatedObject scope:FSCalendarScopeMonth];
NSInteger currentSection = [self.calendar.calculator indexPathForDate:self.calendar.currentPage scope:FSCalendarScopeMonth].section;
return indexPath.section == currentSection;
}]];
NSDate *date = visibleCandidates.firstObject;
date;
});
NSInteger focusedRow = [self.calendar.calculator coordinateForIndexPath:[self.calendar.calculator indexPathForDate:focusedDate scope:FSCalendarScopeMonth]].row;
NSDate *currentPage = self.calendar.currentPage;
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:currentPage scope:FSCalendarScopeMonth];
NSDate *monthHead = [self.calendar.calculator monthHeadForSection:indexPath.section];
NSDate *targetPage = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitDay value:focusedRow*7 toDate:monthHead options:0];
attributes.focusedRowNumber = focusedRow;
attributes.focusedDate = focusedDate;
attributes.targetPage = targetPage;
attributes.targetBounds = [self boundingRectForScope:FSCalendarScopeWeek page:attributes.targetPage];
break;
}
case FSCalendarTransitionWeekToMonth: {
NSInteger focusedRow = 0;
NSDate *currentPage = self.calendar.currentPage;
NSDate *focusedDate = ({
NSArray<NSDate *> *candidates = ({
NSMutableArray *dates = self.calendar.selectedDates.reverseObjectEnumerator.allObjects.mutableCopy;
if (self.calendar.today) {
[dates addObject:self.calendar.today];
}
if (self.calendar.currentPage) {
[dates addObject:[self.calendar.gregorian fs_lastDayOfWeek:currentPage]];
}
dates.copy;
});
NSArray<NSDate *> *visibleCandidates = [candidates filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDate * _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:evaluatedObject scope:FSCalendarScopeWeek];
NSInteger currentSection = [self.calendar.calculator indexPathForDate:self.calendar.currentPage scope:FSCalendarScopeWeek].section;
return indexPath.section == currentSection;
}]];
NSDate *date = visibleCandidates.firstObject;
date;
});
NSDate *firstDayOfMonth = [self.calendar.gregorian fs_firstDayOfMonth:focusedDate];
attributes.focusedDate = focusedDate;
firstDayOfMonth = firstDayOfMonth ?: [self.calendar.gregorian fs_firstDayOfMonth:currentPage];
NSInteger numberOfPlaceholdersForPrev = [self.calendar.calculator numberOfHeadPlaceholdersForMonth:firstDayOfMonth];
NSDate *firstDateOfPage = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitDay value:-numberOfPlaceholdersForPrev toDate:firstDayOfMonth options:0];
for (int i = 0; i < 6; i++) {
NSDate *currentRow = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitWeekOfYear value:i toDate:firstDateOfPage options:0];
if ([self.calendar.gregorian isDate:currentRow inSameDayAsDate:currentPage]) {
focusedRow = i;
currentPage = firstDayOfMonth;
break;
}
attributes.targetScope = targetScope;
attributes.focusedDate = ({
NSArray<NSDate *> *candidates = ({
NSMutableArray *dates = self.calendar.selectedDates.reverseObjectEnumerator.allObjects.mutableCopy;
if (self.calendar.today) {
[dates addObject:self.calendar.today];
}
attributes.focusedRowNumber = focusedRow;
attributes.targetPage = currentPage;
attributes.firstDayOfMonth = firstDayOfMonth;
attributes.targetBounds = [self boundingRectForScope:FSCalendarScopeMonth page:attributes.targetPage];
break;
}
default:
break;
}
if (targetScope == FSCalendarScopeWeek) {
[dates addObject:self.calendar.currentPage];
} else {
[dates addObject:[self.calendar.gregorian dateByAddingUnit:NSCalendarUnitDay value:3 toDate:self.calendar.currentPage options:0]];
}
dates.copy;
});
NSArray<NSDate *> *visibleCandidates = [candidates filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDate * _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:evaluatedObject scope:1-targetScope];
NSInteger currentSection = [self.calendar.calculator indexPathForDate:self.calendar.currentPage scope:1-targetScope].section;
return indexPath.section == currentSection;
}]];
NSDate *date = visibleCandidates.firstObject;
date;
});
attributes.focusedRow = ({
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:attributes.focusedDate scope:FSCalendarScopeMonth];
FSCalendarCoordinate coordinate = [self.calendar.calculator coordinateForIndexPath:indexPath];
coordinate.row;
});
attributes.targetPage = ({
NSDate *targetPage = targetScope == FSCalendarScopeMonth ? [self.calendar.gregorian fs_firstDayOfMonth:attributes.focusedDate] : [self.calendar.gregorian fs_middleDayOfWeek:attributes.focusedDate];
targetPage;
});
attributes.targetBounds = [self boundingRectForScope:attributes.targetScope page:attributes.targetPage];
return attributes;
}
@ -518,15 +303,7 @@
CGSize contentSize;
switch (scope) {
case FSCalendarScopeMonth: {
if (self.calendar.placeholderType == FSCalendarPlaceholderTypeFillSixRows) {
contentSize = self.cachedMonthSize;
} else {
CGFloat padding = self.calendar.collectionViewLayout.sectionInsets.top + self.calendar.collectionViewLayout.sectionInsets.bottom;
contentSize = CGSizeMake(self.calendar.fs_width,
self.calendar.preferredHeaderHeight+
self.calendar.preferredWeekdayHeight+
([self.calendar.calculator numberOfRowsInMonth:page]*self.calendar.collectionViewLayout.estimatedItemSize.height)+padding);
}
contentSize = self.calendar.adjustsBoundingRectWhenChangingMonths ? [self.calendar sizeThatFits:self.calendar.frame.size scope:scope] : self.cachedMonthSize;
break;
}
case FSCalendarScopeWeek: {
@ -534,7 +311,7 @@
break;
}
}
return (CGRect){CGPointZero,contentSize};
return (CGRect){CGPointZero, contentSize};
}
- (void)boundingRectWillChange:(CGRect)targetBounds animated:(BOOL)animated
@ -544,197 +321,103 @@
[[self.calendar valueForKey:@"delegateProxy"] calendar:self.calendar boundingRectWillChange:targetBounds animated:animated];
}
- (void)performForwardTransition:(FSCalendarTransition)transition fromProgress:(CGFloat)progress
- (void)performTransition:(FSCalendarScope)targetScope fromProgress:(CGFloat)fromProgress toProgress:(CGFloat)toProgress animated:(BOOL)animated
{
FSCalendarTransitionAttributes *attr = self.pendingAttributes;
switch (transition) {
case FSCalendarTransitionMonthToWeek: {
[self.calendar willChangeValueForKey:@"scope"];
[self.calendar fs_setUnsignedIntegerVariable:FSCalendarScopeWeek forKey:@"_scope"];
[self.calendar didChangeValueForKey:@"scope"];
[self.calendar fs_setVariable:attr.targetPage forKey:@"_currentPage"];
self.calendar.contentView.clipsToBounds = YES;
CGFloat currentAlpha = MAX(1-progress*1.1,0);
CGFloat duration = 0.3;
[self performAlphaAnimationFrom:currentAlpha to:0 duration:0.22 exception:attr.focusedRowNumber completion:^{
FSCalendarTransitionAttributes *attr = self.transitionAttributes;
[self.calendar willChangeValueForKey:@"scope"];
[self.calendar fs_setUnsignedIntegerVariable:targetScope forKey:@"_scope"];
if (targetScope == FSCalendarScopeWeek) {
[self.calendar fs_setVariable:attr.targetPage forKey:@"_currentPage"];
}
[self.calendar didChangeValueForKey:@"scope"];
if (animated) {
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)])) {
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
[self performAlphaAnimationWithProgress:toProgress];
self.collectionView.fs_top = [self calculateOffsetForProgress:toProgress];
[self boundingRectWillChange:attr.targetBounds animated:YES];
} completion:^(BOOL finished) {
[self performTransitionCompletionAnimated:YES];
}];
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:duration];
self.collectionView.fs_top = -attr.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height;
[self boundingRectWillChange:attr.targetBounds animated:YES];
[UIView commitAnimations];
}
break;
}
case FSCalendarTransitionWeekToMonth: {
[self.calendar willChangeValueForKey:@"scope"];
[self.calendar fs_setUnsignedIntegerVariable:FSCalendarScopeMonth forKey:@"_scope"];
[self.calendar didChangeValueForKey:@"scope"];
[self performAlphaAnimationFrom:progress to:1 duration:0.4 exception:attr.focusedRowNumber completion:^{
[self performTransitionCompletionAnimated:YES];
}];
CGFloat duration = 0.3;
[CATransaction begin];
[CATransaction setDisableActions:NO];
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
[UIView setAnimationsEnabled:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:duration];
self.collectionView.fs_top = 0;
[self boundingRectWillChange:attr.targetBounds animated:YES];
[UIView commitAnimations];
}
[CATransaction commit];
break;
}
default:
break;
}
}
- (void)performBackwardTransition:(FSCalendarTransition)transition fromProgress:(CGFloat)progress
{
switch (transition) {
case FSCalendarTransitionMonthToWeek: {
[self.calendar willChangeValueForKey:@"scope"];
[self.calendar fs_setUnsignedIntegerVariable:FSCalendarScopeMonth forKey:@"_scope"];
[self.calendar didChangeValueForKey:@"scope"];
[self performAlphaAnimationFrom:MAX(1-progress*1.1,0) to:1 duration:0.3 exception:self.pendingAttributes.focusedRowNumber completion:^{
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(__kindof UICollectionViewCell * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
obj.contentView.layer.opacity = 1;
[obj.contentView.layer removeAnimationForKey:@"opacity"];
}];
self.pendingAttributes = nil;
self.state = FSCalendarTransitionStateIdle;
}];
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
[UIView setAnimationsEnabled:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.3];
self.collectionView.fs_top = 0;
[self boundingRectWillChange:self.pendingAttributes.sourceBounds animated:YES];
[UIView commitAnimations];
}
break;
}
case FSCalendarTransitionWeekToMonth: {
[self.calendar willChangeValueForKey:@"scope"];
[self.calendar fs_setUnsignedIntegerVariable:FSCalendarScopeWeek forKey:@"_scope"];
[self.calendar didChangeValueForKey:@"scope"];
[self performAlphaAnimationFrom:progress to:0 duration:0.3 exception:self.pendingAttributes.focusedRowNumber completion:^{
[self.calendar fs_setVariable:self.pendingAttributes.sourcePage forKey:@"_currentPage"];
[self performTransitionCompletion:FSCalendarTransitionMonthToWeek animated:YES];
}];
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
[UIView setAnimationsEnabled:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.3];
self.collectionView.fs_top = (-self.pendingAttributes.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height);
[self boundingRectWillChange:self.pendingAttributes.sourceBounds animated:YES];
[UIView commitAnimations];
}
break;
}
default:
break;
}
}
- (void)performAlphaAnimationFrom:(CGFloat)fromAlpha to:(CGFloat)toAlpha duration:(CGFloat)duration exception:(NSInteger)exception completion:(void(^)(void))completion;
{
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(FSCalendarCell *cell, NSUInteger idx, BOOL *stop) {
if (CGRectContainsPoint(self.collectionView.bounds, cell.center)) {
BOOL shouldPerformAlpha = NO;
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
NSInteger row = [self.calendar.calculator coordinateForIndexPath:indexPath].row;
shouldPerformAlpha = row != exception;
if (shouldPerformAlpha) {
CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@"opacity"];
opacity.duration = duration;
opacity.fromValue = @(fromAlpha);
opacity.toValue = @(toAlpha);
opacity.removedOnCompletion = NO;
opacity.fillMode = kCAFillModeForwards;
[cell.contentView.layer addAnimation:opacity forKey:@"opacity"];
}
}
}];
if (completion) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
completion();
});
} else {
[self performTransitionCompletionAnimated:animated];
[self boundingRectWillChange:attr.targetBounds animated:animated];
}
}
- (void)performAlphaAnimationWithProgress:(CGFloat)progress
{
CGFloat opacity = self.transition == FSCalendarTransitionMonthToWeek ? MAX((1-progress*1.1),0) : progress;
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(FSCalendarCell *cell, NSUInteger idx, BOOL *stop) {
if (CGRectContainsPoint(self.collectionView.bounds, cell.center)) {
BOOL shouldPerformAlpha = NO;
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
NSInteger row = [self.calendar.calculator coordinateForIndexPath:indexPath].row;
shouldPerformAlpha = row != self.pendingAttributes.focusedRowNumber;
if (shouldPerformAlpha) {
cell.contentView.layer.opacity = opacity;
}
CGFloat opacity = self.transitionAttributes.targetScope == FSCalendarScopeWeek ? MAX((1-progress*1.1), 0) : progress;
NSArray<FSCalendarCell *> *surroundingCells = [self.calendar.visibleCells filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(FSCalendarCell * _Nullable cell, NSDictionary<NSString *,id> * _Nullable bindings) {
if (!CGRectContainsPoint(self.collectionView.bounds, cell.center)) {
return NO;
}
}];
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
NSInteger row = [self.calendar.calculator coordinateForIndexPath:indexPath].row;
return row != self.transitionAttributes.focusedRow;
}]];
[surroundingCells setValue:@(opacity) forKey:@"alpha"];
}
- (void)performPathAnimationWithProgress:(CGFloat)progress
{
CGFloat targetHeight = CGRectGetHeight(self.pendingAttributes.targetBounds);
CGFloat sourceHeight = CGRectGetHeight(self.pendingAttributes.sourceBounds);
CGFloat targetHeight = CGRectGetHeight(self.transitionAttributes.targetBounds);
CGFloat sourceHeight = CGRectGetHeight(self.transitionAttributes.sourceBounds);
CGFloat currentHeight = sourceHeight - (sourceHeight-targetHeight)*progress;
CGRect currentBounds = CGRectMake(0, 0, CGRectGetWidth(self.pendingAttributes.targetBounds), currentHeight);
self.collectionView.fs_top = (-self.pendingAttributes.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height)*(self.transition == FSCalendarTransitionMonthToWeek?progress:(1-progress));
CGRect currentBounds = CGRectMake(0, 0, CGRectGetWidth(self.transitionAttributes.targetBounds), currentHeight);
self.collectionView.fs_top = [self calculateOffsetForProgress:progress];
[self boundingRectWillChange:currentBounds animated:NO];
if (self.transition == FSCalendarTransitionWeekToMonth) {
if (self.transitionAttributes.targetScope == FSCalendarScopeMonth) {
self.calendar.contentView.fs_height = targetHeight;
}
}
- (void)prelayoutForWeekToMonthTransition
- (CGFloat)calculateOffsetForProgress:(CGFloat)progress
{
self.calendar.contentView.clipsToBounds = YES;
self.calendar.contentView.fs_height = CGRectGetHeight(self.pendingAttributes.targetBounds);
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:self.transitionAttributes.focusedDate scope:FSCalendarScopeMonth];
CGRect frame = [self.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath].frame;
CGFloat ratio = self.transitionAttributes.targetScope == FSCalendarScopeWeek ? progress : (1 - progress);
CGFloat offset = (-frame.origin.y + self.collectionViewLayout.sectionInsets.top) * ratio;
return offset;
}
- (void)prepareWeekToMonthTransition
{
[self.calendar fs_setVariable:self.transitionAttributes.targetPage forKey:@"_currentPage"];
self.calendar.contentView.fs_height = CGRectGetHeight(self.transitionAttributes.targetBounds);
self.collectionViewLayout.scrollDirection = (UICollectionViewScrollDirection)self.calendar.scrollDirection;
self.calendar.calendarHeaderView.scrollDirection = self.collectionViewLayout.scrollDirection;
self.calendar.needsAdjustingViewFrame = YES;
[self.calendar setNeedsLayout];
[CATransaction begin];
[CATransaction setDisableActions:NO];
[self.collectionView reloadData];
[self.calendar.calendarHeaderView reloadData];
[self.calendar layoutIfNeeded];
[CATransaction commit];
self.collectionView.fs_top = [self calculateOffsetForProgress:0];
}
@end
@implementation FSCalendarTransitionAttributes
- (void)revert
{
CGRect tempRect = self.sourceBounds;
self.sourceBounds = self.targetBounds;
self.targetBounds = tempRect;
NSDate *tempDate = self.sourcePage;
self.sourcePage = self.targetPage;
self.targetPage = tempDate;
self.targetScope = 1 - self.targetScope;
}
@end