Add 'adjustsBoundRectWhenMonthChanges' property to FSCalendar.
This commit is contained in:
parent
eebc82594d
commit
1be7ecbeb2
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue