diff --git a/Demo/ObjCDemo/SCAppDelegate.m b/Demo/ObjCDemo/SCAppDelegate.m index 9313473..2fd124e 100644 --- a/Demo/ObjCDemo/SCAppDelegate.m +++ b/Demo/ObjCDemo/SCAppDelegate.m @@ -13,10 +13,10 @@ @implementation SCAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - self.window.rootViewController = [[SCRootViewController alloc] initWithNibName:NSStringFromClass([SCRootViewController class]) bundle:nil]; - [self.window makeKeyAndVisible]; - return YES; + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window.rootViewController = [[SCRootViewController alloc] initWithNibName:NSStringFromClass([SCRootViewController class]) bundle:nil]; + [self.window makeKeyAndVisible]; + return YES; } @end diff --git a/Demo/ObjCDemo/SCMainViewController.h b/Demo/ObjCDemo/SCMainViewController.h index 320763e..1537b36 100644 --- a/Demo/ObjCDemo/SCMainViewController.h +++ b/Demo/ObjCDemo/SCMainViewController.h @@ -11,11 +11,11 @@ #import "SCEasingFunction.h" typedef enum { - SCPageLayouterTypePlain, - SCPageLayouterTypeSliding, - SCPageLayouterTypeParallax, - SCPageLayouterTypeCards, - SCPageLayouterTypeCount + SCPageLayouterTypePlain, + SCPageLayouterTypeSliding, + SCPageLayouterTypeParallax, + SCPageLayouterTypeCards, + SCPageLayouterTypeCount } SCPageLayouterType; @protocol SCMainViewControllerDelegate; diff --git a/Demo/ObjCDemo/SCMainViewController.m b/Demo/ObjCDemo/SCMainViewController.m index c841682..7bf4c2a 100644 --- a/Demo/ObjCDemo/SCMainViewController.m +++ b/Demo/ObjCDemo/SCMainViewController.m @@ -9,9 +9,9 @@ #import "SCMainViewController.h" typedef NS_ENUM(NSUInteger, SCPickerViewComponentType) { - SCPickerViewComponentTypeLayouter, - SCPickerViewComponentTypeEasingFunction, - SCPickerViewComponentTypeAnimationDuration + SCPickerViewComponentTypeLayouter, + SCPickerViewComponentTypeEasingFunction, + SCPickerViewComponentTypeAnimationDuration }; @interface SCMainViewController () @@ -29,204 +29,204 @@ @implementation SCMainViewController - (IBAction)onPreviousButtonTap:(id)sender { - if([self.delegate respondsToSelector:@selector(mainViewControllerDidRequestNavigationToPreviousPage:)]) { - [self.delegate mainViewControllerDidRequestNavigationToPreviousPage:self]; - } + if([self.delegate respondsToSelector:@selector(mainViewControllerDidRequestNavigationToPreviousPage:)]) { + [self.delegate mainViewControllerDidRequestNavigationToPreviousPage:self]; + } } - (IBAction)onNextButtonTap:(id)sender { - if([self.delegate respondsToSelector:@selector(mainViewControllerDidRequestNavigationToNextPage:)]) { - [self.delegate mainViewControllerDidRequestNavigationToNextPage:self]; - } + if([self.delegate respondsToSelector:@selector(mainViewControllerDidRequestNavigationToNextPage:)]) { + [self.delegate mainViewControllerDidRequestNavigationToNextPage:self]; + } } - (IBAction)onInsertButtonTap:(id)sender { - if([self.delegate respondsToSelector:@selector(mainViewControllerDidRequestPageInsertion:)]) { - [self.delegate mainViewControllerDidRequestPageInsertion:self]; - } + if([self.delegate respondsToSelector:@selector(mainViewControllerDidRequestPageInsertion:)]) { + [self.delegate mainViewControllerDidRequestPageInsertion:self]; + } } - (IBAction)onDeleteButtonTap:(id)sender { - if([self.delegate respondsToSelector:@selector(mainViewControllerDidRequestPageDeletion:)]) { - [self.delegate mainViewControllerDidRequestPageDeletion:self]; - } + if([self.delegate respondsToSelector:@selector(mainViewControllerDidRequestPageDeletion:)]) { + [self.delegate mainViewControllerDidRequestPageDeletion:self]; + } } #pragma mark - UIPickerViewDataSource - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { - return SCPickerViewComponentTypeAnimationDuration + 1; + return SCPickerViewComponentTypeAnimationDuration + 1; } - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { - switch ((SCPickerViewComponentType)component) { - case SCPickerViewComponentTypeLayouter: - return SCPageLayouterTypeCount; - case SCPickerViewComponentTypeEasingFunction: - return SCEasingFunctionTypeBounceEaseInOut + 1; - case SCPickerViewComponentTypeAnimationDuration: - return 40; - } + switch ((SCPickerViewComponentType)component) { + case SCPickerViewComponentTypeLayouter: + return SCPageLayouterTypeCount; + case SCPickerViewComponentTypeEasingFunction: + return SCEasingFunctionTypeBounceEaseInOut + 1; + case SCPickerViewComponentTypeAnimationDuration: + return 40; + } } - (NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component { - UIFont *font = [UIFont fontWithName:@"Menlo" size:18.0f]; - UIColor *color = [UIColor colorWithWhite:1.0f alpha:1.0f]; - - switch ((SCPickerViewComponentType)component) { - case SCPickerViewComponentTypeLayouter: { - static NSDictionary *typeToString; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - typeToString = (@{@(SCPageLayouterTypePlain) : @"Plain", - @(SCPageLayouterTypeSliding) : @"Sliding", - @(SCPageLayouterTypeParallax) : @"Parallax", - @(SCPageLayouterTypeCards) : @"Cards"}); - }); - - return [[NSAttributedString alloc] initWithString:typeToString[@(row)] - attributes:@{NSFontAttributeName: font, NSForegroundColorAttributeName : color}]; - } - case SCPickerViewComponentTypeEasingFunction: - { - static NSDictionary *typeToString; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - typeToString = (@{@(SCEasingFunctionTypeLinear) : @"Linear", - - @(SCEasingFunctionTypeQuadraticEaseIn) : @"Quadratic Ease In", - @(SCEasingFunctionTypeQuadraticEaseOut) : @"Quadratic Ease Out", - @(SCEasingFunctionTypeQuadraticEaseInOut) : @"Quadratic Ease In Out", - - @(SCEasingFunctionTypeCubicEaseIn) : @"Cubic Ease In", - @(SCEasingFunctionTypeCubicEaseOut) : @"Cubic Ease Out", - @(SCEasingFunctionTypeCubicEaseInOut) : @"Cubic Ease In Out", - - @(SCEasingFunctionTypeQuarticEaseIn) : @"Quartic Ease In", - @(SCEasingFunctionTypeQuarticEaseOut) : @"Quartic Ease Out", - @(SCEasingFunctionTypeQuarticEaseInOut) : @"Quartic Ease In Out", - - @(SCEasingFunctionTypeQuinticEaseIn) : @"Quintic Ease In", - @(SCEasingFunctionTypeQuinticEaseOut) : @"Quintic Ease Out", - @(SCEasingFunctionTypeQuinticEaseInOut) : @"Quintic Ease In Out", - - @(SCEasingFunctionTypeSineEaseIn) : @"Sine Ease In", - @(SCEasingFunctionTypeSineEaseOut) : @"Sine Ease Out", - @(SCEasingFunctionTypeSineEaseInOut) : @"Sine Ease In Out", - - @(SCEasingFunctionTypeCircularEaseIn) : @"Circular Ease In", - @(SCEasingFunctionTypeCircularEaseOut) : @"Circular Ease Out", - @(SCEasingFunctionTypeCircularEaseInOut) : @"Circular Ease In Out", - - @(SCEasingFunctionTypeExponentialEaseIn) : @"Exponential Ease In", - @(SCEasingFunctionTypeExponentialEaseOut) : @"Exponential Ease Out", - @(SCEasingFunctionTypeExponentialEaseInOut) : @"Exponential Ease In Out", - - @(SCEasingFunctionTypeElasticEaseIn) : @"Elastic Ease In", - @(SCEasingFunctionTypeElasticEaseOut) : @"Elastic Ease Out", - @(SCEasingFunctionTypeElasticEaseInOut) : @"Elastic Ease In Out", - - @(SCEasingFunctionTypeBackEaseIn) : @"Back Ease In", - @(SCEasingFunctionTypeBackEaseOut) : @"Back Ease Out", - @(SCEasingFunctionTypeBackEaseInOut) : @"Back Ease In Out", - - @(SCEasingFunctionTypeBounceEaseIn) : @"Bounce Ease In", - @(SCEasingFunctionTypeBounceEaseOut) : @"Bounce Ease Out", - @(SCEasingFunctionTypeBounceEaseInOut) : @"Bounce Ease In Out"}); - }); - - return [[NSAttributedString alloc] initWithString:typeToString[@(row)] - attributes:@{NSFontAttributeName: font, NSForegroundColorAttributeName : color}]; - } - case SCPickerViewComponentTypeAnimationDuration: - { - return [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%.2f", [self _rowToDuration:row]] - attributes:@{NSFontAttributeName: font, NSForegroundColorAttributeName : color}]; - } - } + UIFont *font = [UIFont fontWithName:@"Menlo" size:18.0f]; + UIColor *color = [UIColor colorWithWhite:1.0f alpha:1.0f]; + + switch ((SCPickerViewComponentType)component) { + case SCPickerViewComponentTypeLayouter: { + static NSDictionary *typeToString; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + typeToString = (@{@(SCPageLayouterTypePlain) : @"Plain", + @(SCPageLayouterTypeSliding) : @"Sliding", + @(SCPageLayouterTypeParallax) : @"Parallax", + @(SCPageLayouterTypeCards) : @"Cards"}); + }); + + return [[NSAttributedString alloc] initWithString:typeToString[@(row)] + attributes:@{NSFontAttributeName: font, NSForegroundColorAttributeName : color}]; + } + case SCPickerViewComponentTypeEasingFunction: + { + static NSDictionary *typeToString; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + typeToString = (@{@(SCEasingFunctionTypeLinear) : @"Linear", + + @(SCEasingFunctionTypeQuadraticEaseIn) : @"Quadratic Ease In", + @(SCEasingFunctionTypeQuadraticEaseOut) : @"Quadratic Ease Out", + @(SCEasingFunctionTypeQuadraticEaseInOut) : @"Quadratic Ease In Out", + + @(SCEasingFunctionTypeCubicEaseIn) : @"Cubic Ease In", + @(SCEasingFunctionTypeCubicEaseOut) : @"Cubic Ease Out", + @(SCEasingFunctionTypeCubicEaseInOut) : @"Cubic Ease In Out", + + @(SCEasingFunctionTypeQuarticEaseIn) : @"Quartic Ease In", + @(SCEasingFunctionTypeQuarticEaseOut) : @"Quartic Ease Out", + @(SCEasingFunctionTypeQuarticEaseInOut) : @"Quartic Ease In Out", + + @(SCEasingFunctionTypeQuinticEaseIn) : @"Quintic Ease In", + @(SCEasingFunctionTypeQuinticEaseOut) : @"Quintic Ease Out", + @(SCEasingFunctionTypeQuinticEaseInOut) : @"Quintic Ease In Out", + + @(SCEasingFunctionTypeSineEaseIn) : @"Sine Ease In", + @(SCEasingFunctionTypeSineEaseOut) : @"Sine Ease Out", + @(SCEasingFunctionTypeSineEaseInOut) : @"Sine Ease In Out", + + @(SCEasingFunctionTypeCircularEaseIn) : @"Circular Ease In", + @(SCEasingFunctionTypeCircularEaseOut) : @"Circular Ease Out", + @(SCEasingFunctionTypeCircularEaseInOut) : @"Circular Ease In Out", + + @(SCEasingFunctionTypeExponentialEaseIn) : @"Exponential Ease In", + @(SCEasingFunctionTypeExponentialEaseOut) : @"Exponential Ease Out", + @(SCEasingFunctionTypeExponentialEaseInOut) : @"Exponential Ease In Out", + + @(SCEasingFunctionTypeElasticEaseIn) : @"Elastic Ease In", + @(SCEasingFunctionTypeElasticEaseOut) : @"Elastic Ease Out", + @(SCEasingFunctionTypeElasticEaseInOut) : @"Elastic Ease In Out", + + @(SCEasingFunctionTypeBackEaseIn) : @"Back Ease In", + @(SCEasingFunctionTypeBackEaseOut) : @"Back Ease Out", + @(SCEasingFunctionTypeBackEaseInOut) : @"Back Ease In Out", + + @(SCEasingFunctionTypeBounceEaseIn) : @"Bounce Ease In", + @(SCEasingFunctionTypeBounceEaseOut) : @"Bounce Ease Out", + @(SCEasingFunctionTypeBounceEaseInOut) : @"Bounce Ease In Out"}); + }); + + return [[NSAttributedString alloc] initWithString:typeToString[@(row)] + attributes:@{NSFontAttributeName: font, NSForegroundColorAttributeName : color}]; + } + case SCPickerViewComponentTypeAnimationDuration: + { + return [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%.2f", [self _rowToDuration:row]] + attributes:@{NSFontAttributeName: font, NSForegroundColorAttributeName : color}]; + } + } } #pragma mark - UIPickerViewDelegate - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { - switch ((SCPickerViewComponentType)component) { - case SCPickerViewComponentTypeLayouter: - { - self.layouterType = (SCPageLayouterType)row; - - if([self.delegate respondsToSelector:@selector(mainViewControllerDidChangeLayouterType:)]) { - [self.delegate mainViewControllerDidChangeLayouterType:self]; - } - break; - } - case SCPickerViewComponentTypeEasingFunction: - { - self.easingFunctionType = (SCEasingFunctionType)row; - - if([self.delegate respondsToSelector:@selector(mainViewControllerDidChangeAnimationType:)]) { - [self.delegate mainViewControllerDidChangeAnimationType:self]; - } - break; - } - case SCPickerViewComponentTypeAnimationDuration: - { - self.duration = [self _rowToDuration:row]; - - if([self.delegate respondsToSelector:@selector(mainViewControllerDidChangeAnimationDuration:)]) { - [self.delegate mainViewControllerDidChangeAnimationDuration:self]; - } - break; - } - } + switch ((SCPickerViewComponentType)component) { + case SCPickerViewComponentTypeLayouter: + { + self.layouterType = (SCPageLayouterType)row; + + if([self.delegate respondsToSelector:@selector(mainViewControllerDidChangeLayouterType:)]) { + [self.delegate mainViewControllerDidChangeLayouterType:self]; + } + break; + } + case SCPickerViewComponentTypeEasingFunction: + { + self.easingFunctionType = (SCEasingFunctionType)row; + + if([self.delegate respondsToSelector:@selector(mainViewControllerDidChangeAnimationType:)]) { + [self.delegate mainViewControllerDidChangeAnimationType:self]; + } + break; + } + case SCPickerViewComponentTypeAnimationDuration: + { + self.duration = [self _rowToDuration:row]; + + if([self.delegate respondsToSelector:@selector(mainViewControllerDidChangeAnimationDuration:)]) { + [self.delegate mainViewControllerDidChangeAnimationDuration:self]; + } + break; + } + } } - (NSTimeInterval)_rowToDuration:(NSUInteger)row { - return 0.25f * (row + 1); + return 0.25f * (row + 1); } - (NSUInteger)_durationToRow:(NSTimeInterval)duration { - return duration/0.25f - 1; + return duration/0.25f - 1; } #pragma mark - Setters - (SCPageLayouterType)layouterType { - return (SCPageLayouterType)[self.pickerView selectedRowInComponent:SCPickerViewComponentTypeLayouter]; + return (SCPageLayouterType)[self.pickerView selectedRowInComponent:SCPickerViewComponentTypeLayouter]; } - (void)setLayouterType:(SCPageLayouterType)layouterType { - [self.pickerView selectRow:layouterType inComponent:SCPickerViewComponentTypeLayouter animated:NO]; + [self.pickerView selectRow:layouterType inComponent:SCPickerViewComponentTypeLayouter animated:NO]; } - (SCEasingFunctionType)easingFunctionType { - return (SCEasingFunctionType)[self.pickerView selectedRowInComponent:SCPickerViewComponentTypeEasingFunction]; + return (SCEasingFunctionType)[self.pickerView selectedRowInComponent:SCPickerViewComponentTypeEasingFunction]; } - (void)setEasingFunctionType:(SCEasingFunctionType)easingFunctionType { - [self.pickerView selectRow:easingFunctionType inComponent:SCPickerViewComponentTypeEasingFunction animated:NO]; + [self.pickerView selectRow:easingFunctionType inComponent:SCPickerViewComponentTypeEasingFunction animated:NO]; } - (NSTimeInterval)duration { - return [self _rowToDuration:[self.pickerView selectedRowInComponent:SCPickerViewComponentTypeAnimationDuration]]; + return [self _rowToDuration:[self.pickerView selectedRowInComponent:SCPickerViewComponentTypeAnimationDuration]]; } - (void)setDuration:(NSTimeInterval)duration { - [self.pickerView selectRow:[self _durationToRow:duration] inComponent:SCPickerViewComponentTypeAnimationDuration animated:NO]; + [self.pickerView selectRow:[self _durationToRow:duration] inComponent:SCPickerViewComponentTypeAnimationDuration animated:NO]; } @end diff --git a/Demo/ObjCDemo/SCRootViewController.m b/Demo/ObjCDemo/SCRootViewController.m index dea7a52..d550b9a 100644 --- a/Demo/ObjCDemo/SCRootViewController.m +++ b/Demo/ObjCDemo/SCRootViewController.m @@ -34,24 +34,24 @@ @implementation SCRootViewController - (void)viewDidLoad { - [super viewDidLoad]; - - self.viewControllers = [NSMutableArray array]; - for(int i=0; i < kDefaultNumberOfPages; i++) { - [self.viewControllers addObject:[NSNull null]]; - } - - self.pageViewController = [[SCPageViewController alloc] init]; - [self.pageViewController setDataSource:self]; - [self.pageViewController setDelegate:self]; - - [self.pageViewController setLayouter:[[SCPageLayouter alloc] init] animated:NO completion:nil]; - [self.pageViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeLinear]]; + [super viewDidLoad]; - [self addChildViewController:self.pageViewController]; - [self.pageViewController.view setFrame:self.view.bounds]; - [self.view addSubview:self.pageViewController.view]; - [self.pageViewController didMoveToParentViewController:self]; + self.viewControllers = [NSMutableArray array]; + for(int i=0; i < kDefaultNumberOfPages; i++) { + [self.viewControllers addObject:[NSNull null]]; + } + + self.pageViewController = [[SCPageViewController alloc] init]; + [self.pageViewController setDataSource:self]; + [self.pageViewController setDelegate:self]; + + [self.pageViewController setLayouter:[[SCPageLayouter alloc] init] animated:NO completion:nil]; + [self.pageViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeLinear]]; + + [self addChildViewController:self.pageViewController]; + [self.pageViewController.view setFrame:self.view.bounds]; + [self.view addSubview:self.pageViewController.view]; + [self.pageViewController didMoveToParentViewController:self]; [self _updateViewControllerDetails]; } @@ -60,23 +60,23 @@ - (void)viewDidLoad - (NSUInteger)numberOfPagesInPageViewController:(SCPageViewController *)pageViewController { - return self.viewControllers.count; + return self.viewControllers.count; } - (UIViewController *)pageViewController:(SCPageViewController *)pageViewController viewControllerForPageAtIndex:(NSUInteger)pageIndex { - SCMainViewController *viewController = self.viewControllers[pageIndex]; - - if([viewController isEqual:[NSNull null]]) { - viewController = [[SCMainViewController alloc] init]; - [viewController.view setFrame:self.view.bounds]; - [viewController setDelegate:self]; - [viewController.contentView setBackgroundColor:[UIColor randomColor]]; - - [self.viewControllers replaceObjectAtIndex:pageIndex withObject:viewController]; - } - - return viewController; + SCMainViewController *viewController = self.viewControllers[pageIndex]; + + if([viewController isEqual:[NSNull null]]) { + viewController = [[SCMainViewController alloc] init]; + [viewController.view setFrame:self.view.bounds]; + [viewController setDelegate:self]; + [viewController.contentView setBackgroundColor:[UIColor randomColor]]; + + [self.viewControllers replaceObjectAtIndex:pageIndex withObject:viewController]; + } + + return viewController; } - (NSUInteger)initialPageInPageViewController:(SCPageViewController *)pageViewController @@ -88,127 +88,127 @@ - (NSUInteger)initialPageInPageViewController:(SCPageViewController *)pageViewCo - (void)pageViewController:(SCPageViewController *)pageViewController didNavigateToOffset:(CGPoint)offset { - [self _updateViewControllerDetails]; + [self _updateViewControllerDetails]; } #pragma mark - SCMainViewControllerDelegate - (void)mainViewControllerDidChangeLayouterType:(SCMainViewController *)mainViewController { - static NSDictionary *typeToLayouter; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - typeToLayouter = (@{@(SCPageLayouterTypePlain) : [SCPageLayouter class], - @(SCPageLayouterTypeSliding) : [SCSlidingPageLayouter class], - @(SCPageLayouterTypeParallax) : [SCParallaxPageLayouter class], - @(SCPageLayouterTypeCards) : [SCCardsPageLayouter class]}); - }); - - id pageLayouter = [[typeToLayouter[@(mainViewController.layouterType)] alloc] init]; - [self.pageViewController setLayouter:pageLayouter animated:YES completion:nil]; - - [self _updateViewControllerDetails]; + static NSDictionary *typeToLayouter; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + typeToLayouter = (@{@(SCPageLayouterTypePlain) : [SCPageLayouter class], + @(SCPageLayouterTypeSliding) : [SCSlidingPageLayouter class], + @(SCPageLayouterTypeParallax) : [SCParallaxPageLayouter class], + @(SCPageLayouterTypeCards) : [SCCardsPageLayouter class]}); + }); + + id pageLayouter = [[typeToLayouter[@(mainViewController.layouterType)] alloc] init]; + [self.pageViewController setLayouter:pageLayouter animated:YES completion:nil]; + + [self _updateViewControllerDetails]; } - (void)mainViewControllerDidChangeAnimationType:(SCMainViewController *)mainViewController { - [self.pageViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:mainViewController.easingFunctionType]]; - self.selectedEasingFunctionType = mainViewController.easingFunctionType; - - [self _updateViewControllerDetails]; + [self.pageViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:mainViewController.easingFunctionType]]; + self.selectedEasingFunctionType = mainViewController.easingFunctionType; + + [self _updateViewControllerDetails]; } - (void)mainViewControllerDidChangeAnimationDuration:(SCMainViewController *)mainViewController { - [self.pageViewController setAnimationDuration:mainViewController.duration]; + [self.pageViewController setAnimationDuration:mainViewController.duration]; } - (void)mainViewControllerDidRequestNavigationToNextPage:(SCMainViewController *)mainViewController { - [self.pageViewController navigateToPageAtIndex:MIN(self.pageViewController.numberOfPages, self.pageViewController.currentPage + 1) animated:YES completion:nil]; + [self.pageViewController navigateToPageAtIndex:MIN(self.pageViewController.numberOfPages, self.pageViewController.currentPage + 1) animated:YES completion:nil]; } - (void)mainViewControllerDidRequestNavigationToPreviousPage:(SCMainViewController *)mainViewController { - [self.pageViewController navigateToPageAtIndex:MAX(0, self.pageViewController.currentPage - 1) animated:YES completion:nil]; + [self.pageViewController navigateToPageAtIndex:MAX(0, self.pageViewController.currentPage - 1) animated:YES completion:nil]; } - (void)mainViewControllerDidRequestPageInsertion:(SCMainViewController *)mainViewController { - [self _insertPagesAtIndexes:[NSIndexSet indexSetWithIndex:[self.viewControllers indexOfObject:mainViewController]]]; - [self _updateViewControllerDetails]; + [self _insertPagesAtIndexes:[NSIndexSet indexSetWithIndex:[self.viewControllers indexOfObject:mainViewController]]]; + [self _updateViewControllerDetails]; } - (void)mainViewControllerDidRequestPageDeletion:(SCMainViewController *)mainViewController { - [self _deletePagesAtIndexes:[NSIndexSet indexSetWithIndex:[self.viewControllers indexOfObject:mainViewController]]]; - [self _updateViewControllerDetails]; + [self _deletePagesAtIndexes:[NSIndexSet indexSetWithIndex:[self.viewControllers indexOfObject:mainViewController]]]; + [self _updateViewControllerDetails]; } #pragma mark - Private - (void)_reloadPagesAtIndexes:(NSIndexSet *)indexes { - [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { - [self.viewControllers replaceObjectAtIndex:idx withObject:[NSNull null]]; - }]; - - [self.pageViewController reloadPagesAtIndexes:indexes animated:YES completion:nil]; + [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + [self.viewControllers replaceObjectAtIndex:idx withObject:[NSNull null]]; + }]; + + [self.pageViewController reloadPagesAtIndexes:indexes animated:YES completion:nil]; } - (void)_insertPagesAtIndexes:(NSIndexSet *)indexes { - [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { - [self.viewControllers insertObject:[NSNull null] atIndex:idx]; - }]; - - [self.pageViewController insertPagesAtIndexes:indexes animated:YES completion:nil]; + [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + [self.viewControllers insertObject:[NSNull null] atIndex:idx]; + }]; + + [self.pageViewController insertPagesAtIndexes:indexes animated:YES completion:nil]; } - (void)_deletePagesAtIndexes:(NSIndexSet *)indexes { - [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger idx, BOOL *stop) { - [self.viewControllers removeObjectAtIndex:idx]; - }]; - - [self.pageViewController deletePagesAtIndexes:indexes animated:YES completion:nil]; + [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger idx, BOOL *stop) { + [self.viewControllers removeObjectAtIndex:idx]; + }]; + + [self.pageViewController deletePagesAtIndexes:indexes animated:YES completion:nil]; } - (void)_movePageFromIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex { - UIViewController *viewController = [self.viewControllers objectAtIndex:fromIndex]; - [self.viewControllers removeObjectAtIndex:fromIndex]; - [self.viewControllers insertObject:viewController atIndex:toIndex]; - - [self.pageViewController movePageAtIndex:fromIndex toIndex:toIndex animated:YES completion:nil]; + UIViewController *viewController = [self.viewControllers objectAtIndex:fromIndex]; + [self.viewControllers removeObjectAtIndex:fromIndex]; + [self.viewControllers insertObject:viewController atIndex:toIndex]; + + [self.pageViewController movePageAtIndex:fromIndex toIndex:toIndex animated:YES completion:nil]; } - (void)_updateViewControllerDetails { - [self.viewControllers enumerateObjectsUsingBlock:^(SCMainViewController *controller, NSUInteger index, BOOL *stop) { - - if([controller isEqual:[NSNull null]]) { - return; - } - - [controller.visiblePercentageLabel setText:[NSString stringWithFormat:@"%.2f%%", [self.pageViewController visiblePercentageForViewController:controller]]]; - - [controller.pageNumberLabel setText:[NSString stringWithFormat:@"Page %lu of %lu", (unsigned long)index, (unsigned long)self.pageViewController.numberOfPages]]; - - [controller setEasingFunctionType:self.selectedEasingFunctionType]; - [controller setDuration:self.pageViewController.animationDuration]; - - static NSDictionary *layouterToType; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - layouterToType = (@{NSStringFromClass([SCPageLayouter class]) : @(SCPageLayouterTypePlain), - NSStringFromClass([SCSlidingPageLayouter class]) : @(SCPageLayouterTypeSliding), - NSStringFromClass([SCParallaxPageLayouter class]) : @(SCPageLayouterTypeParallax), - NSStringFromClass([SCCardsPageLayouter class]) : @(SCPageLayouterTypeCards)}); - }); - - [controller setLayouterType:(SCPageLayouterType)[layouterToType[NSStringFromClass([self.pageViewController.layouter class])] unsignedIntegerValue]]; - }]; + [self.viewControllers enumerateObjectsUsingBlock:^(SCMainViewController *controller, NSUInteger index, BOOL *stop) { + + if([controller isEqual:[NSNull null]]) { + return; + } + + [controller.visiblePercentageLabel setText:[NSString stringWithFormat:@"%.2f%%", [self.pageViewController visiblePercentageForViewController:controller]]]; + + [controller.pageNumberLabel setText:[NSString stringWithFormat:@"Page %lu of %lu", (unsigned long)index, (unsigned long)self.pageViewController.numberOfPages]]; + + [controller setEasingFunctionType:self.selectedEasingFunctionType]; + [controller setDuration:self.pageViewController.animationDuration]; + + static NSDictionary *layouterToType; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + layouterToType = (@{NSStringFromClass([SCPageLayouter class]) : @(SCPageLayouterTypePlain), + NSStringFromClass([SCSlidingPageLayouter class]) : @(SCPageLayouterTypeSliding), + NSStringFromClass([SCParallaxPageLayouter class]) : @(SCPageLayouterTypeParallax), + NSStringFromClass([SCCardsPageLayouter class]) : @(SCPageLayouterTypeCards)}); + }); + + [controller setLayouterType:(SCPageLayouterType)[layouterToType[NSStringFromClass([self.pageViewController.layouter class])] unsignedIntegerValue]]; + }]; } @end diff --git a/Demo/SwiftDemo/MainViewController.swift b/Demo/SwiftDemo/MainViewController.swift index 9577eb3..4588a02 100644 --- a/Demo/SwiftDemo/MainViewController.swift +++ b/Demo/SwiftDemo/MainViewController.swift @@ -17,7 +17,7 @@ enum PageLayouterType : Int { } class MainViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate { - + @IBOutlet var pickerView:UIPickerView! var delegate:MainViewControllerDelegate? @@ -40,7 +40,7 @@ class MainViewController: UIViewController, UIPickerViewDataSource, UIPickerView } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { - + let layouterType = PageLayouterType.init(rawValue: row)! switch(layouterType) { case .plain: diff --git a/Demo/SwiftDemo/RootViewController.swift b/Demo/SwiftDemo/RootViewController.swift index b4f1dd6..d888365 100644 --- a/Demo/SwiftDemo/RootViewController.swift +++ b/Demo/SwiftDemo/RootViewController.swift @@ -29,7 +29,7 @@ class RootViewController : UIViewController , SCPageViewControllerDataSource, SC self.pageViewController.dataSource = self; self.pageViewController.delegate = self; - + self.addChildViewController(self.pageViewController) self.pageViewController.view.frame = self.view.bounds self.view.addSubview(self.pageViewController.view) @@ -43,7 +43,7 @@ class RootViewController : UIViewController , SCPageViewControllerDataSource, SC } func pageViewController(_ pageViewController: SCPageViewController!, viewControllerForPageAt pageIndex: UInt) -> UIViewController! { - + if let viewController = self.viewControllers[Int(pageIndex)] { return viewController @@ -69,7 +69,7 @@ class RootViewController : UIViewController , SCPageViewControllerDataSource, SC //MARK: - SCPageViewControllerDelegate func pageViewController(_ pageViewController: SCPageViewController!, didNavigateToOffset offset: CGPoint) { - + func layouterToType(layouter: SCPageLayouterProtocol) -> PageLayouterType { switch layouter { case is SCSlidingPageLayouter: diff --git a/Demo/Tests/SCPageLayouterTests.m b/Demo/Tests/SCPageLayouterTests.m index c295e3b..303800e 100644 --- a/Demo/Tests/SCPageLayouterTests.m +++ b/Demo/Tests/SCPageLayouterTests.m @@ -22,118 +22,118 @@ @implementation SCPageLayouterTests - (void)setUp { - [super setUp]; - - self.pageViewController = [[SCPageViewController alloc] init]; - [self.pageViewController.view setFrame:CGRectMake(0, 0, 1024, 768)]; - - self.pageLayouter = [[SCPageLayouter alloc] init]; + [super setUp]; + + self.pageViewController = [[SCPageViewController alloc] init]; + [self.pageViewController.view setFrame:CGRectMake(0, 0, 1024, 768)]; + + self.pageLayouter = [[SCPageLayouter alloc] init]; } - (void)testHorizontalPageFrame_01 { - [self.pageLayouter setInterItemSpacing:0.0f]; - - CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:0 pageViewController:self.pageViewController]; - CGRect expectedFrame = CGRectMake(0, 0, CGRectGetWidth(self.pageViewController.view.bounds), CGRectGetHeight(self.pageViewController.view.bounds)); - - XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); + [self.pageLayouter setInterItemSpacing:0.0f]; + + CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:0 pageViewController:self.pageViewController]; + CGRect expectedFrame = CGRectMake(0, 0, CGRectGetWidth(self.pageViewController.view.bounds), CGRectGetHeight(self.pageViewController.view.bounds)); + + XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); } - (void)testHorizontalPageFrame_02 { - [self.pageLayouter setInterItemSpacing:-100.0f]; - - CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:1 pageViewController:self.pageViewController]; - CGRect expectedFrame = CGRectMake(CGRectGetWidth(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing, - 0, - CGRectGetWidth(self.pageViewController.view.bounds), - CGRectGetHeight(self.pageViewController.view.bounds)); - - XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); + [self.pageLayouter setInterItemSpacing:-100.0f]; + + CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:1 pageViewController:self.pageViewController]; + CGRect expectedFrame = CGRectMake(CGRectGetWidth(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing, + 0, + CGRectGetWidth(self.pageViewController.view.bounds), + CGRectGetHeight(self.pageViewController.view.bounds)); + + XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); } - (void)testHorizontalPageFrame_03 { - [self.pageLayouter setInterItemSpacing:10.0f]; - - CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:1 pageViewController:self.pageViewController]; - CGRect expectedFrame = CGRectMake(CGRectGetWidth(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing, - 0, - CGRectGetWidth(self.pageViewController.view.bounds), - CGRectGetHeight(self.pageViewController.view.bounds)); - - XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); + [self.pageLayouter setInterItemSpacing:10.0f]; + + CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:1 pageViewController:self.pageViewController]; + CGRect expectedFrame = CGRectMake(CGRectGetWidth(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing, + 0, + CGRectGetWidth(self.pageViewController.view.bounds), + CGRectGetHeight(self.pageViewController.view.bounds)); + + XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); } - (void)testHorizontalPageFrame_04 { - [self.pageLayouter setInterItemSpacing:123.0f]; - - NSUInteger pageIndex = 123; - - CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:123 pageViewController:self.pageViewController]; - CGRect expectedFrame = CGRectMake((CGRectGetWidth(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing) * pageIndex, - 0, - CGRectGetWidth(self.pageViewController.view.bounds), - CGRectGetHeight(self.pageViewController.view.bounds)); - - XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); + [self.pageLayouter setInterItemSpacing:123.0f]; + + NSUInteger pageIndex = 123; + + CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:123 pageViewController:self.pageViewController]; + CGRect expectedFrame = CGRectMake((CGRectGetWidth(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing) * pageIndex, + 0, + CGRectGetWidth(self.pageViewController.view.bounds), + CGRectGetHeight(self.pageViewController.view.bounds)); + + XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); } - (void)testVerticalPageFrame_01 { - [self.pageLayouter setNavigationType:SCPageLayouterNavigationTypeVertical]; - [self.pageLayouter setInterItemSpacing:0.0f]; - - CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:0 pageViewController:self.pageViewController]; - CGRect expectedFrame = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.pageViewController.view.bounds), CGRectGetHeight(self.pageViewController.view.bounds)); - - XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); + [self.pageLayouter setNavigationType:SCPageLayouterNavigationTypeVertical]; + [self.pageLayouter setInterItemSpacing:0.0f]; + + CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:0 pageViewController:self.pageViewController]; + CGRect expectedFrame = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.pageViewController.view.bounds), CGRectGetHeight(self.pageViewController.view.bounds)); + + XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); } - (void)testVerticalPageFrame_02 { - [self.pageLayouter setNavigationType:SCPageLayouterNavigationTypeVertical]; - [self.pageLayouter setInterItemSpacing:-100.0f]; - - CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:1 pageViewController:self.pageViewController]; - CGRect expectedFrame = CGRectMake(0.0f, - CGRectGetHeight(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing, - CGRectGetWidth(self.pageViewController.view.bounds), - CGRectGetHeight(self.pageViewController.view.bounds)); - - XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); + [self.pageLayouter setNavigationType:SCPageLayouterNavigationTypeVertical]; + [self.pageLayouter setInterItemSpacing:-100.0f]; + + CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:1 pageViewController:self.pageViewController]; + CGRect expectedFrame = CGRectMake(0.0f, + CGRectGetHeight(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing, + CGRectGetWidth(self.pageViewController.view.bounds), + CGRectGetHeight(self.pageViewController.view.bounds)); + + XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); } - (void)testVerticalPageFrame_03 { - [self.pageLayouter setNavigationType:SCPageLayouterNavigationTypeVertical]; - [self.pageLayouter setInterItemSpacing:10.0f]; - - CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:1 pageViewController:self.pageViewController]; - CGRect expectedFrame = CGRectMake(0.0f, - CGRectGetHeight(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing, - CGRectGetWidth(self.pageViewController.view.bounds), - CGRectGetHeight(self.pageViewController.view.bounds)); - - XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); + [self.pageLayouter setNavigationType:SCPageLayouterNavigationTypeVertical]; + [self.pageLayouter setInterItemSpacing:10.0f]; + + CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:1 pageViewController:self.pageViewController]; + CGRect expectedFrame = CGRectMake(0.0f, + CGRectGetHeight(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing, + CGRectGetWidth(self.pageViewController.view.bounds), + CGRectGetHeight(self.pageViewController.view.bounds)); + + XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); } - (void)testVerticalPageFrame_04 { - [self.pageLayouter setNavigationType:SCPageLayouterNavigationTypeVertical]; - [self.pageLayouter setInterItemSpacing:123.0f]; - - NSUInteger pageIndex = 123; - - CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:123 pageViewController:self.pageViewController]; - CGRect expectedFrame = CGRectMake(0.0f, - (CGRectGetHeight(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing) * pageIndex, - CGRectGetWidth(self.pageViewController.view.bounds), - CGRectGetHeight(self.pageViewController.view.bounds)); - - XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); + [self.pageLayouter setNavigationType:SCPageLayouterNavigationTypeVertical]; + [self.pageLayouter setInterItemSpacing:123.0f]; + + NSUInteger pageIndex = 123; + + CGRect finalFrame = [self.pageLayouter finalFrameForPageAtIndex:123 pageViewController:self.pageViewController]; + CGRect expectedFrame = CGRectMake(0.0f, + (CGRectGetHeight(self.pageViewController.view.bounds) + self.pageLayouter.interItemSpacing) * pageIndex, + CGRectGetWidth(self.pageViewController.view.bounds), + CGRectGetHeight(self.pageViewController.view.bounds)); + + XCTAssert(CGRectEqualToRect(finalFrame, expectedFrame)); } diff --git a/Demo/Tests/SCPageViewControllerTests.m b/Demo/Tests/SCPageViewControllerTests.m index 8da5a74..16a98ba 100644 --- a/Demo/Tests/SCPageViewControllerTests.m +++ b/Demo/Tests/SCPageViewControllerTests.m @@ -26,92 +26,92 @@ @implementation SCPageViewControllerTests - (void)setUp { - [super setUp]; - - self.viewControllers = [NSMutableArray array]; - for(int i=0; i<10; i++) { - [self.viewControllers addObject:[NSNull null]]; - } - - self.pageViewController = [[SCPageViewController alloc] init]; - [self.pageViewController setDataSource:self]; - - self.layouter = [[SCPageLayouter alloc] init]; - [self.pageViewController setLayouter:self.layouter animated:NO completion:nil]; - - UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController; - - [self.pageViewController willMoveToParentViewController:rootViewController]; - - [rootViewController addChildViewController:self.pageViewController]; - - [rootViewController.view addSubview:self.pageViewController.view]; - [self.pageViewController.view setFrame:rootViewController.view.bounds]; - - [self.pageViewController didMoveToParentViewController:rootViewController]; + [super setUp]; + + self.viewControllers = [NSMutableArray array]; + for(int i=0; i<10; i++) { + [self.viewControllers addObject:[NSNull null]]; + } + + self.pageViewController = [[SCPageViewController alloc] init]; + [self.pageViewController setDataSource:self]; + + self.layouter = [[SCPageLayouter alloc] init]; + [self.pageViewController setLayouter:self.layouter animated:NO completion:nil]; + + UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController; + + [self.pageViewController willMoveToParentViewController:rootViewController]; + + [rootViewController addChildViewController:self.pageViewController]; + + [rootViewController.view addSubview:self.pageViewController.view]; + [self.pageViewController.view setFrame:rootViewController.view.bounds]; + + [self.pageViewController didMoveToParentViewController:rootViewController]; } - (void)tearDown { [super tearDown]; - - [self.pageViewController willMoveToParentViewController:nil]; - [self.pageViewController.view removeFromSuperview]; - [self.pageViewController removeFromParentViewController]; + + [self.pageViewController willMoveToParentViewController:nil]; + [self.pageViewController.view removeFromSuperview]; + [self.pageViewController removeFromParentViewController]; } - (void)testStartupProperties_01 { - XCTAssert(CGRectEqualToRect(self.pageViewController.view.bounds, [UIScreen mainScreen].bounds)); - XCTAssert(self.pageViewController.numberOfPages == 10); - XCTAssert(self.pageViewController.currentPage == 0); - XCTAssert(self.pageViewController.loadedViewControllers.count == 2); + XCTAssert(CGRectEqualToRect(self.pageViewController.view.bounds, [UIScreen mainScreen].bounds)); + XCTAssert(self.pageViewController.numberOfPages == 10); + XCTAssert(self.pageViewController.currentPage == 0); + XCTAssert(self.pageViewController.loadedViewControllers.count == 2); } - (void)testNavigation_01 { - [self.pageViewController navigateToPageAtIndex:1 animated:NO completion:nil]; - - XCTAssert(self.pageViewController.currentPage == 1); - XCTAssert(self.pageViewController.visibleViewControllers.count == 1); - XCTAssert([self.pageViewController.visibleViewControllers.firstObject isEqual:self.viewControllers[self.pageViewController.currentPage]]); - - [self.pageViewController navigateToPageAtIndex:3 animated:NO completion:nil]; - - XCTAssert(self.pageViewController.currentPage == 3); - XCTAssert(self.pageViewController.visibleViewControllers.count == 1); - XCTAssert([self.pageViewController.visibleViewControllers.firstObject isEqual:self.viewControllers[self.pageViewController.currentPage]]); - XCTAssert(self.pageViewController.loadedViewControllers.count == 3); + [self.pageViewController navigateToPageAtIndex:1 animated:NO completion:nil]; + + XCTAssert(self.pageViewController.currentPage == 1); + XCTAssert(self.pageViewController.visibleViewControllers.count == 1); + XCTAssert([self.pageViewController.visibleViewControllers.firstObject isEqual:self.viewControllers[self.pageViewController.currentPage]]); + + [self.pageViewController navigateToPageAtIndex:3 animated:NO completion:nil]; + + XCTAssert(self.pageViewController.currentPage == 3); + XCTAssert(self.pageViewController.visibleViewControllers.count == 1); + XCTAssert([self.pageViewController.visibleViewControllers.firstObject isEqual:self.viewControllers[self.pageViewController.currentPage]]); + XCTAssert(self.pageViewController.loadedViewControllers.count == 3); } - (void)testNavigation_02 { - [self.pageViewController navigateToPageAtIndex:(self.pageViewController.numberOfPages - 1) animated:NO completion:nil]; - - XCTAssert(self.pageViewController.currentPage == (self.pageViewController.numberOfPages - 1)); - XCTAssert(self.pageViewController.visibleViewControllers.count == 1); - XCTAssert([self.pageViewController.visibleViewControllers.firstObject isEqual:self.viewControllers[self.pageViewController.currentPage]]); - XCTAssert(self.pageViewController.loadedViewControllers.count == 2); + [self.pageViewController navigateToPageAtIndex:(self.pageViewController.numberOfPages - 1) animated:NO completion:nil]; + + XCTAssert(self.pageViewController.currentPage == (self.pageViewController.numberOfPages - 1)); + XCTAssert(self.pageViewController.visibleViewControllers.count == 1); + XCTAssert([self.pageViewController.visibleViewControllers.firstObject isEqual:self.viewControllers[self.pageViewController.currentPage]]); + XCTAssert(self.pageViewController.loadedViewControllers.count == 2); } #pragma mark - SCPageViewControllerDataSource - (NSUInteger)numberOfPagesInPageViewController:(SCPageViewController *)pageViewController { - return kDefaultNumberOfPages; + return kDefaultNumberOfPages; } - (UIViewController *)pageViewController:(SCPageViewController *)pageViewController - viewControllerForPageAtIndex:(NSUInteger)pageIndex + viewControllerForPageAtIndex:(NSUInteger)pageIndex { - UIViewController *viewController = self.viewControllers[pageIndex]; - - if(!viewController || [viewController isEqual:[NSNull null]]) { - viewController = [[UIViewController alloc] init]; - [self.viewControllers replaceObjectAtIndex:pageIndex withObject:viewController]; - } - - return viewController; + UIViewController *viewController = self.viewControllers[pageIndex]; + + if(!viewController || [viewController isEqual:[NSNull null]]) { + viewController = [[UIViewController alloc] init]; + [self.viewControllers replaceObjectAtIndex:pageIndex withObject:viewController]; + } + + return viewController; } @end diff --git a/SCPageViewController/Layouters/SCCardsPageLayouter.m b/SCPageViewController/Layouters/SCCardsPageLayouter.m index 5c7b7b7..24df6b9 100644 --- a/SCPageViewController/Layouters/SCCardsPageLayouter.m +++ b/SCPageViewController/Layouters/SCCardsPageLayouter.m @@ -16,58 +16,58 @@ @implementation SCCardsPageLayouter - (instancetype)init { - if(self = [super init]) { - - self.numberOfPagesToPreloadBeforeCurrentPage = 3; - self.numberOfPagesToPreloadAfterCurrentPage = 3; - - self.navigationConstraintType = SCPageLayouterNavigationContraintTypeForward | SCPageLayouterNavigationContraintTypeReverse; - - self.pagePercentage = 0.5f; - } - - return self; + if(self = [super init]) { + + self.numberOfPagesToPreloadBeforeCurrentPage = 3; + self.numberOfPagesToPreloadAfterCurrentPage = 3; + + self.navigationConstraintType = SCPageLayouterNavigationContraintTypeForward | SCPageLayouterNavigationContraintTypeReverse; + + self.pagePercentage = 0.5f; + } + + return self; } - (UIEdgeInsets)contentInsetForPageViewController:(SCPageViewController *)pageViewController { - CGRect frame = pageViewController.view.bounds; - CGFloat verticalInset = floor(CGRectGetHeight(frame) - CGRectGetHeight(frame) * self.pagePercentage); - CGFloat horizontalInset = floor(CGRectGetWidth(frame) - CGRectGetWidth(frame) * self.pagePercentage); - - return UIEdgeInsetsMake(verticalInset/2.0f, horizontalInset/2.0f, verticalInset/2.0f, horizontalInset/2.0f); + CGRect frame = pageViewController.view.bounds; + CGFloat verticalInset = floor(CGRectGetHeight(frame) - CGRectGetHeight(frame) * self.pagePercentage); + CGFloat horizontalInset = floor(CGRectGetWidth(frame) - CGRectGetWidth(frame) * self.pagePercentage); + + return UIEdgeInsetsMake(verticalInset/2.0f, horizontalInset/2.0f, verticalInset/2.0f, horizontalInset/2.0f); } - (CGFloat)interItemSpacingForPageViewController:(SCPageViewController *)pageViewController { - switch (self.navigationType) { - case SCPageLayouterNavigationTypeHorizontal: { - self.interItemSpacing = floor(CGRectGetWidth(pageViewController.view.bounds)/100.0f); - } - case SCPageLayouterNavigationTypeVertical: { - self.interItemSpacing = floor(CGRectGetHeight(pageViewController.view.bounds)/100.0f); - } - } - - return self.interItemSpacing; + switch (self.navigationType) { + case SCPageLayouterNavigationTypeHorizontal: { + self.interItemSpacing = floor(CGRectGetWidth(pageViewController.view.bounds)/100.0f); + } + case SCPageLayouterNavigationTypeVertical: { + self.interItemSpacing = floor(CGRectGetHeight(pageViewController.view.bounds)/100.0f); + } + } + + return self.interItemSpacing; } - (CGRect)finalFrameForPageAtIndex:(NSUInteger)index - pageViewController:(SCPageViewController *)pageViewController + pageViewController:(SCPageViewController *)pageViewController { - CGRect frame = pageViewController.view.bounds; - frame.size.height = frame.size.height * self.pagePercentage; - frame.size.width = frame.size.width * self.pagePercentage; - - if(self.navigationType == SCPageLayouterNavigationTypeVertical) { - frame.origin.y = index * (CGRectGetHeight(frame) + self.interItemSpacing); - frame.origin.x = CGRectGetMidX(pageViewController.view.bounds) - CGRectGetMidX(frame); - } else { - frame.origin.x = index * (CGRectGetWidth(frame) + self.interItemSpacing); - frame.origin.y = CGRectGetMidY(pageViewController.view.bounds) - CGRectGetMidY(frame); - } - - return frame; + CGRect frame = pageViewController.view.bounds; + frame.size.height = frame.size.height * self.pagePercentage; + frame.size.width = frame.size.width * self.pagePercentage; + + if(self.navigationType == SCPageLayouterNavigationTypeVertical) { + frame.origin.y = index * (CGRectGetHeight(frame) + self.interItemSpacing); + frame.origin.x = CGRectGetMidX(pageViewController.view.bounds) - CGRectGetMidX(frame); + } else { + frame.origin.x = index * (CGRectGetWidth(frame) + self.interItemSpacing); + frame.origin.y = CGRectGetMidY(pageViewController.view.bounds) - CGRectGetMidY(frame); + } + + return frame; } @end diff --git a/SCPageViewController/Layouters/SCPageLayouter.m b/SCPageViewController/Layouters/SCPageLayouter.m index 3f68eec..fdc24bc 100644 --- a/SCPageViewController/Layouters/SCPageLayouter.m +++ b/SCPageViewController/Layouters/SCPageLayouter.m @@ -20,75 +20,75 @@ @implementation SCPageLayouter - (id)init { - if(self = [super init]) { - - self.navigationType = SCPageLayouterNavigationTypeHorizontal; - self.navigationConstraintType = SCPageLayouterNavigationContraintTypeForward | SCPageLayouterNavigationContraintTypeReverse; - - self.numberOfPagesToPreloadBeforeCurrentPage = 1; - self.numberOfPagesToPreloadAfterCurrentPage = 1; - - self.interItemSpacing = 50.0f; - } - - return self; + if(self = [super init]) { + + self.navigationType = SCPageLayouterNavigationTypeHorizontal; + self.navigationConstraintType = SCPageLayouterNavigationContraintTypeForward | SCPageLayouterNavigationContraintTypeReverse; + + self.numberOfPagesToPreloadBeforeCurrentPage = 1; + self.numberOfPagesToPreloadAfterCurrentPage = 1; + + self.interItemSpacing = 50.0f; + } + + return self; } - (CGFloat)interItemSpacingForPageViewController:(SCPageViewController *)pageViewController { - return self.interItemSpacing; + return self.interItemSpacing; } - (CGRect)finalFrameForPageAtIndex:(NSUInteger)index - pageViewController:(SCPageViewController *)pageViewController + pageViewController:(SCPageViewController *)pageViewController { - CGRect frame = pageViewController.view.bounds; - - if(self.navigationType == SCPageLayouterNavigationTypeVertical) { - frame.origin.y = index * (CGRectGetHeight(frame) + self.interItemSpacing); - } else { - frame.origin.x = index * (CGRectGetWidth(frame) + self.interItemSpacing); - } - - return frame; + CGRect frame = pageViewController.view.bounds; + + if(self.navigationType == SCPageLayouterNavigationTypeVertical) { + frame.origin.y = index * (CGRectGetHeight(frame) + self.interItemSpacing); + } else { + frame.origin.x = index * (CGRectGetWidth(frame) + self.interItemSpacing); + } + + return frame; } - (void)animatePageReloadAtIndex:(NSUInteger)index - oldViewController:(UIViewController *)oldViewController - newViewController:(UIViewController *)newViewController - pageViewController:(SCPageViewController *)pageViewController - completion:(void (^)(void))completion + oldViewController:(UIViewController *)oldViewController + newViewController:(UIViewController *)newViewController + pageViewController:(SCPageViewController *)pageViewController + completion:(void (^)(void))completion { - [newViewController.view setAlpha:0.0f]; - [UIView animateWithDuration:pageViewController.animationDuration delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ - [oldViewController.view setAlpha:0.0f]; - [newViewController.view setAlpha:1.0f]; - } completion:^(BOOL finished) { - completion(); - }]; + [newViewController.view setAlpha:0.0f]; + [UIView animateWithDuration:pageViewController.animationDuration delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ + [oldViewController.view setAlpha:0.0f]; + [newViewController.view setAlpha:1.0f]; + } completion:^(BOOL finished) { + completion(); + }]; } - (void)animatePageInsertionAtIndex:(NSUInteger)index - viewController:(UIViewController *)viewController - pageViewController:(SCPageViewController *)pageViewController - completion:(void (^)(void))completion + viewController:(UIViewController *)viewController + pageViewController:(SCPageViewController *)pageViewController + completion:(void (^)(void))completion { - CGRect frame = viewController.view.frame; - - if(self.navigationType == SCPageLayouterNavigationTypeHorizontal) { - [viewController.view setFrame:CGRectOffset(frame, 0.0f, CGRectGetHeight(frame))]; - } else { - [viewController.view setFrame:CGRectOffset(frame, CGRectGetWidth(frame), 0.0f)]; - } - - [viewController.view setAlpha:0.0f]; - - [UIView animateWithDuration:pageViewController.animationDuration delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ - [viewController.view setFrame:frame]; - [viewController.view setAlpha:1.0f]; - } completion:^(BOOL finished) { - completion(); - }]; + CGRect frame = viewController.view.frame; + + if(self.navigationType == SCPageLayouterNavigationTypeHorizontal) { + [viewController.view setFrame:CGRectOffset(frame, 0.0f, CGRectGetHeight(frame))]; + } else { + [viewController.view setFrame:CGRectOffset(frame, CGRectGetWidth(frame), 0.0f)]; + } + + [viewController.view setAlpha:0.0f]; + + [UIView animateWithDuration:pageViewController.animationDuration delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ + [viewController.view setFrame:frame]; + [viewController.view setAlpha:1.0f]; + } completion:^(BOOL finished) { + completion(); + }]; } - (BOOL)shouldPreserveOffsetForInsertionAtIndex:(NSUInteger)index pageViewController:(SCPageViewController *)pageViewController @@ -97,37 +97,37 @@ - (BOOL)shouldPreserveOffsetForInsertionAtIndex:(NSUInteger)index pageViewContro } - (void)animatePageDeletionAtIndex:(NSUInteger)index - viewController:(UIViewController *)viewController - pageViewController:(SCPageViewController *)pageViewController - completion:(void (^)(void))completion + viewController:(UIViewController *)viewController + pageViewController:(SCPageViewController *)pageViewController + completion:(void (^)(void))completion { - [UIView animateWithDuration:pageViewController.animationDuration delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ - - if(self.navigationType == SCPageLayouterNavigationTypeHorizontal) { - [viewController.view setFrame:CGRectOffset(viewController.view.frame, 0.0f, CGRectGetHeight(viewController.view.bounds))]; - } else { - [viewController.view setFrame:CGRectOffset(viewController.view.frame, CGRectGetWidth(viewController.view.bounds), 0.0f)]; - } - - [viewController.view setAlpha:0.0f]; - } completion:^(BOOL finished) { - completion(); - }]; + [UIView animateWithDuration:pageViewController.animationDuration delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ + + if(self.navigationType == SCPageLayouterNavigationTypeHorizontal) { + [viewController.view setFrame:CGRectOffset(viewController.view.frame, 0.0f, CGRectGetHeight(viewController.view.bounds))]; + } else { + [viewController.view setFrame:CGRectOffset(viewController.view.frame, CGRectGetWidth(viewController.view.bounds), 0.0f)]; + } + + [viewController.view setAlpha:0.0f]; + } completion:^(BOOL finished) { + completion(); + }]; } - (void)animatePageMoveFromIndex:(NSUInteger)fromIndex - toIndex:(NSUInteger)toIndex - viewController:(UIViewController *)viewController - pageViewController:(SCPageViewController *)pageViewController - completion:(void (^)(void))completion + toIndex:(NSUInteger)toIndex + viewController:(UIViewController *)viewController + pageViewController:(SCPageViewController *)pageViewController + completion:(void (^)(void))completion { - CGRect finalFrame = [self finalFrameForPageAtIndex:toIndex pageViewController:pageViewController]; - - [UIView animateWithDuration:pageViewController.animationDuration delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ - [viewController.view setFrame:finalFrame]; - } completion:^(BOOL finished) { - completion(); - }]; + CGRect finalFrame = [self finalFrameForPageAtIndex:toIndex pageViewController:pageViewController]; + + [UIView animateWithDuration:pageViewController.animationDuration delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ + [viewController.view setFrame:finalFrame]; + } completion:^(BOOL finished) { + completion(); + }]; } @end diff --git a/SCPageViewController/Layouters/SCPageLayouterProtocol.h b/SCPageViewController/Layouters/SCPageLayouterProtocol.h index 159a37c..3508aa5 100644 --- a/SCPageViewController/Layouters/SCPageLayouterProtocol.h +++ b/SCPageViewController/Layouters/SCPageLayouterProtocol.h @@ -15,15 +15,15 @@ */ typedef NS_ENUM(NSUInteger, SCPageLayouterNavigationType) { - SCPageLayouterNavigationTypeHorizontal, - SCPageLayouterNavigationTypeVertical, + SCPageLayouterNavigationTypeHorizontal, + SCPageLayouterNavigationTypeVertical, }; /** Navigation contraint types that can be used used when continuous navigation is disabled */ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { - SCPageLayouterNavigationContraintTypeNone = 0, - SCPageLayouterNavigationContraintTypeForward = 1 << 0, /** Scroll view bounces on page bounds only when navigating forward*/ - SCPageLayouterNavigationContraintTypeReverse = 1 << 1 /** Scroll view bounces on page bounds only when navigating backwards*/ + SCPageLayouterNavigationContraintTypeNone = 0, + SCPageLayouterNavigationContraintTypeForward = 1 << 0, /** Scroll view bounces on page bounds only when navigating forward*/ + SCPageLayouterNavigationContraintTypeReverse = 1 << 1 /** Scroll view bounces on page bounds only when navigating backwards*/ }; @@ -57,7 +57,7 @@ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { * @return The frame for the viewController's view */ - (CGRect)finalFrameForPageAtIndex:(NSUInteger)index - pageViewController:(SCPageViewController *)pageViewController; + pageViewController:(SCPageViewController *)pageViewController; @optional @@ -89,9 +89,9 @@ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { * @return The frame for the viewController's view */ - (CGRect)currentFrameForPageAtIndex:(NSUInteger)index - contentOffset:(CGPoint)contentOffset - finalFrame:(CGRect)finalFrame - pageViewController:(SCPageViewController *)pageViewController; + contentOffset:(CGPoint)contentOffset + finalFrame:(CGRect)finalFrame + pageViewController:(SCPageViewController *)pageViewController; /** Defines the z position which should be used when laying out the given view controller @@ -102,7 +102,7 @@ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { * @return the index the view controller we be places at in the view hierarchy */ - (NSUInteger)zPositionForPageAtIndex:(NSUInteger)index - pageViewController:(SCPageViewController *)pageViewController; + pageViewController:(SCPageViewController *)pageViewController; /** Returns the view controller sublayer transformation that should be used @@ -113,8 +113,8 @@ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { * @return The sublayer transformation to be applied */ - (CATransform3D)sublayerTransformForPageAtIndex:(NSUInteger)index - contentOffset:(CGPoint)contentOffset - pageViewController:(SCPageViewController *)pageViewController; + contentOffset:(CGPoint)contentOffset + pageViewController:(SCPageViewController *)pageViewController; /** Method that the pageController calls when its scrollView scrolls @@ -122,7 +122,7 @@ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { * @param offset The current offset in the PageViewController's scrollView */ - (void)pageViewController:(SCPageViewController *)pageViewController - didNavigateToOffset:(CGPoint)offset; + didNavigateToOffset:(CGPoint)offset; /** Called by the pageViewController when it receives a reload page animated @@ -138,10 +138,10 @@ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { * proceed with the layout */ - (void)animatePageReloadAtIndex:(NSUInteger)index - oldViewController:(UIViewController *)oldViewController - newViewController:(UIViewController *)newViewController - pageViewController:(SCPageViewController *)pageViewController - completion:(void(^)(void))completion; + oldViewController:(UIViewController *)oldViewController + newViewController:(UIViewController *)newViewController + pageViewController:(SCPageViewController *)pageViewController + completion:(void(^)(void))completion; /** Called by the pageViewController when it receives an insert page animated @@ -156,9 +156,9 @@ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { * proceed with the layout */ - (void)animatePageInsertionAtIndex:(NSUInteger)index - viewController:(UIViewController *)viewController - pageViewController:(SCPageViewController *)pageViewController - completion:(void(^)(void))completion; + viewController:(UIViewController *)viewController + pageViewController:(SCPageViewController *)pageViewController + completion:(void(^)(void))completion; /** Called bue the pageViewController when inserting a new page that * would affect the current content offset @@ -183,9 +183,9 @@ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { * proceed with the layout */ - (void)animatePageDeletionAtIndex:(NSUInteger)index - viewController:(UIViewController *)viewController - pageViewController:(SCPageViewController *)pageViewController - completion:(void(^)(void))completion; + viewController:(UIViewController *)viewController + pageViewController:(SCPageViewController *)pageViewController + completion:(void(^)(void))completion; /** Called by the pageViewController when it receives an move page animated @@ -201,9 +201,9 @@ typedef NS_OPTIONS(NSUInteger, SCPageLayouterNavigationContraintType) { * proceed with the layout */ - (void)animatePageMoveFromIndex:(NSUInteger)fromIndex - toIndex:(NSUInteger)toIndex - viewController:(UIViewController *)viewController - pageViewController:(SCPageViewController *)pageViewController - completion:(void(^)(void))completion; + toIndex:(NSUInteger)toIndex + viewController:(UIViewController *)viewController + pageViewController:(SCPageViewController *)pageViewController + completion:(void(^)(void))completion; @end diff --git a/SCPageViewController/Layouters/SCParallaxPageLayouter.m b/SCPageViewController/Layouters/SCParallaxPageLayouter.m index dcd189a..dc50f0a 100644 --- a/SCPageViewController/Layouters/SCParallaxPageLayouter.m +++ b/SCPageViewController/Layouters/SCParallaxPageLayouter.m @@ -12,31 +12,31 @@ @implementation SCParallaxPageLayouter - (id)init { - if(self = [super init]) { - self.interItemSpacing = 0.0f; - } - - return self; + if(self = [super init]) { + self.interItemSpacing = 0.0f; + } + + return self; } - (CGRect)currentFrameForPageAtIndex:(NSUInteger)index - contentOffset:(CGPoint)contentOffset - finalFrame:(CGRect)finalFrame - pageViewController:(SCPageViewController *)pageViewController + contentOffset:(CGPoint)contentOffset + finalFrame:(CGRect)finalFrame + pageViewController:(SCPageViewController *)pageViewController { - if(index == 0) { - return finalFrame; - } - - if(self.navigationType == SCPageLayouterNavigationTypeVertical) { - CGFloat ratio = 1.0f - (CGRectGetMinY(finalFrame) - contentOffset.y) / (CGRectGetHeight(finalFrame) + CGRectGetHeight(finalFrame)/2); - finalFrame.origin.y = (CGRectGetMinY(finalFrame) - CGRectGetHeight(finalFrame)) + CGRectGetHeight(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); - } else { - CGFloat ratio = 1.0f - (CGRectGetMinX(finalFrame) - contentOffset.x) / (CGRectGetWidth(finalFrame) + CGRectGetWidth(finalFrame)/2); - finalFrame.origin.x = (CGRectGetMinX(finalFrame) - CGRectGetWidth(finalFrame)) + CGRectGetWidth(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); - } - - return finalFrame; + if(index == 0) { + return finalFrame; + } + + if(self.navigationType == SCPageLayouterNavigationTypeVertical) { + CGFloat ratio = 1.0f - (CGRectGetMinY(finalFrame) - contentOffset.y) / (CGRectGetHeight(finalFrame) + CGRectGetHeight(finalFrame)/2); + finalFrame.origin.y = (CGRectGetMinY(finalFrame) - CGRectGetHeight(finalFrame)) + CGRectGetHeight(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); + } else { + CGFloat ratio = 1.0f - (CGRectGetMinX(finalFrame) - contentOffset.x) / (CGRectGetWidth(finalFrame) + CGRectGetWidth(finalFrame)/2); + finalFrame.origin.x = (CGRectGetMinX(finalFrame) - CGRectGetWidth(finalFrame)) + CGRectGetWidth(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); + } + + return finalFrame; } @end diff --git a/SCPageViewController/Layouters/SCSlidingPageLayouter.m b/SCPageViewController/Layouters/SCSlidingPageLayouter.m index 8619eca..929d9fc 100644 --- a/SCPageViewController/Layouters/SCSlidingPageLayouter.m +++ b/SCPageViewController/Layouters/SCSlidingPageLayouter.m @@ -12,29 +12,29 @@ @implementation SCSlidingPageLayouter - (id)init { - if(self = [super init]) { - self.interItemSpacing = 0.0f; - } - - return self; + if(self = [super init]) { + self.interItemSpacing = 0.0f; + } + + return self; } - (CGRect)currentFrameForPageAtIndex:(NSUInteger)index - contentOffset:(CGPoint)contentOffset - finalFrame:(CGRect)finalFrame - pageViewController:(SCPageViewController *)pageViewController + contentOffset:(CGPoint)contentOffset + finalFrame:(CGRect)finalFrame + pageViewController:(SCPageViewController *)pageViewController { - if(index == 0) { - return finalFrame; - } - - if(self.navigationType == SCPageLayouterNavigationTypeVertical) { - finalFrame.origin.y = MAX(finalFrame.origin.y - finalFrame.size.height, MIN(CGRectGetMaxY(finalFrame) - CGRectGetHeight(finalFrame), contentOffset.y)); - } else { - finalFrame.origin.x = MAX(finalFrame.origin.x - finalFrame.size.width, MIN(CGRectGetMaxX(finalFrame) - CGRectGetWidth(finalFrame), contentOffset.x)); - } - - return finalFrame; + if(index == 0) { + return finalFrame; + } + + if(self.navigationType == SCPageLayouterNavigationTypeVertical) { + finalFrame.origin.y = MAX(finalFrame.origin.y - finalFrame.size.height, MIN(CGRectGetMaxY(finalFrame) - CGRectGetHeight(finalFrame), contentOffset.y)); + } else { + finalFrame.origin.x = MAX(finalFrame.origin.x - finalFrame.size.width, MIN(CGRectGetMaxX(finalFrame) - CGRectGetWidth(finalFrame), contentOffset.x)); + } + + return finalFrame; } @end diff --git a/SCPageViewController/SCPageViewController.h b/SCPageViewController/SCPageViewController.h index 5d4afe3..105d436 100644 --- a/SCPageViewController/SCPageViewController.h +++ b/SCPageViewController/SCPageViewController.h @@ -28,8 +28,8 @@ * @param completion the block to be called when the transition is over */ - (void)setLayouter:(id)layouter - animated:(BOOL)animated - completion:(void(^)(void))completion; + animated:(BOOL)animated + completion:(void(^)(void))completion; /** Sets the layouter and also focuses on the given index @@ -39,9 +39,9 @@ * @param completion the block to be called when the transition is over */ - (void)setLayouter:(id)layouter - andFocusOnIndex:(NSUInteger)pageIndex - animated:(BOOL)animated - completion:(void(^)(void))completion; + andFocusOnIndex:(NSUInteger)pageIndex + animated:(BOOL)animated + completion:(void(^)(void))completion; /** Reloads and re-lays out all the pages */ @@ -87,8 +87,8 @@ * @param completion the block to be called when the navigation finished */ - (void)navigateToPageAtIndex:(NSUInteger)pageIndex - animated:(BOOL)animated - completion:(void(^)(void))completion; + animated:(BOOL)animated + completion:(void(^)(void))completion; /** @@ -238,8 +238,8 @@ * internal scrollView's bounds and not covered by any other view) */ - (void)pageViewController:(SCPageViewController *)pageViewController - didShowViewController:(UIViewController *)controller - atIndex:(NSUInteger)index; + didShowViewController:(UIViewController *)controller + atIndex:(NSUInteger)index; /** Delegate method that the pageController calls when a view controller is hidden @@ -251,8 +251,8 @@ * scrollView bounds or when it is fully overlapped by other views */ - (void)pageViewController:(SCPageViewController *)pageViewController - didHideViewController:(UIViewController *)controller - atIndex:(NSUInteger)index; + didHideViewController:(UIViewController *)controller + atIndex:(NSUInteger)index; /** Delegate method that the pageController calls when its scrollView scrolls @@ -260,7 +260,7 @@ * @param offset The current offset in the PageViewController's scrollView */ - (void)pageViewController:(SCPageViewController *)pageViewController - didNavigateToOffset:(CGPoint)offset; + didNavigateToOffset:(CGPoint)offset; /** Delegate method that the pageController calls when its scrollView rests diff --git a/SCPageViewController/SCPageViewController.m b/SCPageViewController/SCPageViewController.m index 517d935..815accf 100644 --- a/SCPageViewController/SCPageViewController.m +++ b/SCPageViewController/SCPageViewController.m @@ -65,91 +65,91 @@ @implementation SCPageViewController - (void)dealloc { - if(self.isContentOffsetBlocked) { - [self.scrollView removeObserver:self forKeyPath:@"contentOffset"]; - } - - [self.scrollView setDelegate:nil]; + if(self.isContentOffsetBlocked) { + [self.scrollView removeObserver:self forKeyPath:@"contentOffset"]; + } + + [self.scrollView setDelegate:nil]; } - (instancetype)init { - if(self = [super init]) { - [self _commonSetup]; - } - - return self; + if(self = [super init]) { + [self _commonSetup]; + } + + return self; } - (void)awakeFromNib { - [super awakeFromNib]; - [self _commonSetup]; + [super awakeFromNib]; + [self _commonSetup]; } - (void)_commonSetup { - self.pages = [NSMutableArray array]; - self.visibleControllers = [NSMutableArray array]; - self.pagingEnabled = YES; - - self.easingFunction = [SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeSineEaseInOut]; - self.animationDuration = 0.25f; - - self.layouterContentInset = UIEdgeInsetsZero; - self.layouterInterItemSpacing = 0.0f; - - self.scrollView = [[SCScrollView alloc] init]; - self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - self.scrollView.showsVerticalScrollIndicator = NO; - self.scrollView.showsHorizontalScrollIndicator = NO; - self.scrollView.decelerationRate = UIScrollViewDecelerationRateFast; - self.scrollView.delegate = self; - self.scrollView.clipsToBounds = NO; + self.pages = [NSMutableArray array]; + self.visibleControllers = [NSMutableArray array]; + self.pagingEnabled = YES; + + self.easingFunction = [SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeSineEaseInOut]; + self.animationDuration = 0.25f; + + self.layouterContentInset = UIEdgeInsetsZero; + self.layouterInterItemSpacing = 0.0f; + + self.scrollView = [[SCScrollView alloc] init]; + self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.scrollView.showsVerticalScrollIndicator = NO; + self.scrollView.showsHorizontalScrollIndicator = NO; + self.scrollView.decelerationRate = UIScrollViewDecelerationRateFast; + self.scrollView.delegate = self; + self.scrollView.clipsToBounds = NO; } - (void)loadView { - self.view = [[SCPageViewControllerView alloc] init]; - [(SCPageViewControllerView *)self.view setDelegate:self]; + self.view = [[SCPageViewControllerView alloc] init]; + [(SCPageViewControllerView *)self.view setDelegate:self]; } - (void)viewDidLoad { - [super viewDidLoad]; - - [self.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; - - [self.scrollView setFrame:self.view.bounds]; + [super viewDidLoad]; + + [self.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; + + [self.scrollView setFrame:self.view.bounds]; // Prevents _adjustContentOffsetIfNecessary from triggering UIView *scrollViewWrapper = [[UIView alloc] initWithFrame:self.view.bounds]; [scrollViewWrapper setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; [scrollViewWrapper addSubview:self.scrollView]; - [self.view addSubview:scrollViewWrapper]; + [self.view addSubview:scrollViewWrapper]; if([self.dataSource respondsToSelector:@selector(initialPageInPageViewController:)]) { self.initialPageIndex = @([self.dataSource initialPageInPageViewController:self]); self.currentPage = self.initialPageIndex.unsignedIntegerValue; } - [self reloadData]; + [self reloadData]; } - (void)viewWillLayoutSubviews { - [super viewWillLayoutSubviews]; + [super viewWillLayoutSubviews]; - [self setLayouter:self.layouter andFocusOnIndex:self.currentPage animated:NO completion:nil]; + [self setLayouter:self.layouter andFocusOnIndex:self.currentPage animated:NO completion:nil]; } - (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - self.isViewVisible = YES; - [self _tilePages]; + [super viewWillAppear:animated]; + + self.isViewVisible = YES; + [self _tilePages]; } - (void)viewDidAppear:(BOOL)animated @@ -161,484 +161,484 @@ - (void)viewDidAppear:(BOOL)animated - (void)viewDidDisappear:(BOOL)animated { - [super viewDidDisappear:animated]; - - self.isViewVisible = NO; - [self _tilePages]; + [super viewDidDisappear:animated]; + + self.isViewVisible = NO; + [self _tilePages]; } #pragma mark - Public Methods - (void)setLayouter:(id)layouter - andFocusOnIndex:(NSUInteger)pageIndex - animated:(BOOL)animated - completion:(void(^)(void))completion + andFocusOnIndex:(NSUInteger)pageIndex + animated:(BOOL)animated + completion:(void(^)(void))completion { - [self setLayouter:layouter animated:animated completion:^{ - if(completion) { - completion(); - } - }]; - - if(!self.scrollView.isRunningAnimation) { - if(animated) { - [UIView animateWithDuration:self.animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ - [self navigateToPageAtIndex:pageIndex animated:NO completion:nil]; - } completion:nil]; - } else { - [self navigateToPageAtIndex:pageIndex animated:animated completion:nil]; //HERE - } - } + [self setLayouter:layouter animated:animated completion:^{ + if(completion) { + completion(); + } + }]; + + if(!self.scrollView.isRunningAnimation) { + if(animated) { + [UIView animateWithDuration:self.animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ + [self navigateToPageAtIndex:pageIndex animated:NO completion:nil]; + } completion:nil]; + } else { + [self navigateToPageAtIndex:pageIndex animated:animated completion:nil]; + } + } } - (void)setLayouter:(id)layouter - animated:(BOOL)animated - completion:(void (^)(void))completion + animated:(BOOL)animated + completion:(void (^)(void))completion { - self.previousLayouter = self.layouter; - self.layouter = layouter; - - if(!self.isViewLoaded) { - return; // Will attempt tiling on viewDidLoad - } - - void(^updateLayout)(void) = ^{ - [self _blockContentOffsetOnPageAtIndex:self.currentPage]; - [self _updateBoundsAndConstraints]; - [self _unblockContentOffset]; - - [self _sortSubviewsByZPosition]; - [self _tilePages]; + self.previousLayouter = self.layouter; + self.layouter = layouter; + + if(!self.isViewLoaded) { + return; // Will attempt tiling on viewDidLoad + } + + void(^updateLayout)(void) = ^{ + [self _blockContentOffsetOnPageAtIndex:self.currentPage]; + [self _updateBoundsAndConstraints]; + [self _unblockContentOffset]; + + [self _sortSubviewsByZPosition]; + [self _tilePages]; [self.view layoutIfNeeded]; - }; - - if(animated) { - self.isAnimatingLayouterChange = YES; - [UIView animateWithDuration:self.animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ - updateLayout(); - } completion:^(BOOL finished) { - self.isAnimatingLayouterChange = NO; - self.previousLayouter = nil; - if(completion) { - completion(); - } - }]; - } else { - updateLayout(); - if(completion) { - completion(); - } - } + }; + + if(animated) { + self.isAnimatingLayouterChange = YES; + [UIView animateWithDuration:self.animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ + updateLayout(); + } completion:^(BOOL finished) { + self.isAnimatingLayouterChange = NO; + self.previousLayouter = nil; + if(completion) { + completion(); + } + }]; + } else { + updateLayout(); + if(completion) { + completion(); + } + } } - (void)reloadData { - [self.pages enumerateObjectsUsingBlock:^(id obj, NSUInteger pageIndex, BOOL *stop) { - [self _removePageAtIndex:pageIndex]; - }]; - - NSUInteger oldNumberOfPages = self.numberOfPages; - self.numberOfPages = [self.dataSource numberOfPagesInPageViewController:self]; - - [self.pages removeAllObjects]; - for(NSUInteger i = 0; i < self.numberOfPages; i++) { - [self.pages addObject:[NSNull null]]; - } - [self.visibleControllers removeAllObjects]; - - if(oldNumberOfPages >= self.numberOfPages) { - NSUInteger index = MAX(0, (NSInteger)self.numberOfPages - 1); - [self navigateToPageAtIndex:index animated:NO completion:nil]; - } else { - [self _updateBoundsAndConstraints]; - [self _tilePages]; - } + [self.pages enumerateObjectsUsingBlock:^(id obj, NSUInteger pageIndex, BOOL *stop) { + [self _removePageAtIndex:pageIndex]; + }]; + + NSUInteger oldNumberOfPages = self.numberOfPages; + self.numberOfPages = [self.dataSource numberOfPagesInPageViewController:self]; + + [self.pages removeAllObjects]; + for(NSUInteger i = 0; i < self.numberOfPages; i++) { + [self.pages addObject:[NSNull null]]; + } + [self.visibleControllers removeAllObjects]; + + if(oldNumberOfPages >= self.numberOfPages) { + NSUInteger index = MAX(0, (NSInteger)self.numberOfPages - 1); + [self navigateToPageAtIndex:index animated:NO completion:nil]; + } else { + [self _updateBoundsAndConstraints]; + [self _tilePages]; + } } - (void)navigateToPageAtIndex:(NSUInteger)pageIndex - animated:(BOOL)animated - completion:(void(^)(void))completion + animated:(BOOL)animated + completion:(void(^)(void))completion { - NSUInteger previousCurrentPage = self.currentPage; - - if(pageIndex >= self.numberOfPages) { - return; - } - - CGRect frame = CGRectIntegral([self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]); - - CGPoint offset; - if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { - offset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(-1.0f, 0.0f)]; - offset.x -= self.layouterContentInset.left; - } else { - offset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, -1.0f)]; - offset.y -= self.layouterContentInset.top; - } - - offset = CGPointMake((NSInteger)floor(offset.x), (NSInteger)floor(offset.y)); - - void(^animationFinishedBlock)(void) = ^{ - - [self _updateNavigationContraints]; - [self _tilePages]; - - if(!animated && previousCurrentPage != self.currentPage && [self.delegate respondsToSelector:@selector(pageViewController:didNavigateToPageAtIndex:)]) { - [self.delegate pageViewController:self didNavigateToPageAtIndex:pageIndex]; - } - - if(completion) { - completion(); - } - }; - - [self.scrollView setContentOffset:offset easingFunction:self.easingFunction duration:(animated ? self.animationDuration : 0.0f) completion:animationFinishedBlock]; + NSUInteger previousCurrentPage = self.currentPage; + + if(pageIndex >= self.numberOfPages) { + return; + } + + CGRect frame = CGRectIntegral([self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]); + + CGPoint offset; + if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { + offset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(-1.0f, 0.0f)]; + offset.x -= self.layouterContentInset.left; + } else { + offset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, -1.0f)]; + offset.y -= self.layouterContentInset.top; + } + + offset = CGPointMake((NSInteger)floor(offset.x), (NSInteger)floor(offset.y)); + + void(^animationFinishedBlock)(void) = ^{ + + [self _updateNavigationContraints]; + [self _tilePages]; + + if(!animated && previousCurrentPage != self.currentPage && [self.delegate respondsToSelector:@selector(pageViewController:didNavigateToPageAtIndex:)]) { + [self.delegate pageViewController:self didNavigateToPageAtIndex:pageIndex]; + } + + if(completion) { + completion(); + } + }; + + [self.scrollView setContentOffset:offset easingFunction:self.easingFunction duration:(animated ? self.animationDuration : 0.0f) completion:animationFinishedBlock]; } - (NSArray *)loadedViewControllers { - NSMutableArray *array = [NSMutableArray array]; - for(SCPageViewControllerPageDetails *details in self.pages) { - if(![details isEqual:[NSNull null]]) { - if(details.viewController) { - [array addObject:details.viewController]; - } - } - } - - return array; + NSMutableArray *array = [NSMutableArray array]; + for(SCPageViewControllerPageDetails *details in self.pages) { + if(![details isEqual:[NSNull null]]) { + if(details.viewController) { + [array addObject:details.viewController]; + } + } + } + + return array; } - (NSArray *)visibleViewControllers { - return [self.visibleControllers copy]; + return [self.visibleControllers copy]; } - (CGFloat)visiblePercentageForViewController:(UIViewController *)viewController { - for(SCPageViewControllerPageDetails *pageDetails in self.pages) { - if([pageDetails isEqual:[NSNull null]]) { - continue; - } - - if([pageDetails.viewController isEqual:viewController]) { - return pageDetails.visiblePercentage; - } - } - - return 0.0f; + for(SCPageViewControllerPageDetails *pageDetails in self.pages) { + if([pageDetails isEqual:[NSNull null]]) { + continue; + } + + if([pageDetails.viewController isEqual:viewController]) { + return pageDetails.visiblePercentage; + } + } + + return 0.0f; } - (UIViewController *)viewControllerForPageAtIndex:(NSUInteger)pageIndex { - if(pageIndex >= self.pages.count) { - return nil; - } - - SCPageViewControllerPageDetails *pageDetails = [self.pages objectAtIndex:pageIndex]; - - if([pageDetails isEqual:[NSNull null]]) { - return nil; - } - - return pageDetails.viewController; + if(pageIndex >= self.pages.count) { + return nil; + } + + SCPageViewControllerPageDetails *pageDetails = [self.pages objectAtIndex:pageIndex]; + + if([pageDetails isEqual:[NSNull null]]) { + return nil; + } + + return pageDetails.viewController; } - (NSUInteger)pageIndexForViewController:(UIViewController *)viewController { - NSUInteger pageIndex = NSNotFound; - - for(SCPageViewControllerPageDetails *details in self.pages) { - if([details isEqual:[NSNull null]]) { - continue; - } - - if([details.viewController isEqual:viewController]) { - return [self.pages indexOfObject:details]; - } - } - - return pageIndex; + NSUInteger pageIndex = NSNotFound; + + for(SCPageViewControllerPageDetails *details in self.pages) { + if([details isEqual:[NSNull null]]) { + continue; + } + + if([details.viewController isEqual:viewController]) { + return [self.pages indexOfObject:details]; + } + } + + return pageIndex; } - (BOOL)visible { - return self.isViewVisible; + return self.isViewVisible; } #pragma mark - Navigational Constraints - (void)_updateBoundsAndConstraints { - if([self.layouter respondsToSelector:@selector(contentInsetForPageViewController:)]) { - self.layouterContentInset = [self.layouter contentInsetForPageViewController:self]; - } else { - self.layouterContentInset = UIEdgeInsetsZero; - } - - if([self.layouter respondsToSelector:@selector(interItemSpacingForPageViewController:)]) { - self.layouterInterItemSpacing = round([self.layouter interItemSpacingForPageViewController:self]); - } else { - self.layouterInterItemSpacing = 0.0f; - } - - CGRect frame = [self.layouter finalFrameForPageAtIndex:self.numberOfPages - 1 pageViewController:self]; - if(self.layouter.navigationType == SCPageLayouterNavigationTypeVertical) { - [self.scrollView setContentInset:UIEdgeInsetsMake(self.layouterContentInset.top, 0.0f, self.layouterContentInset.bottom, 0.0f)]; - [self.scrollView setContentSize:CGSizeMake(0.0f, round(MAX(CGRectGetHeight(self.scrollView.bounds), CGRectGetMaxY(frame))))]; - } else { - [self.scrollView setContentInset:UIEdgeInsetsMake(0.0f, self.layouterContentInset.left, 0.0f, self.layouterContentInset.right)]; - [self.scrollView setContentSize:CGSizeMake(round(MAX(CGRectGetWidth(self.scrollView.bounds), CGRectGetMaxX(frame))), 0.0f)]; - } - - [self _updateNavigationContraints]; + if([self.layouter respondsToSelector:@selector(contentInsetForPageViewController:)]) { + self.layouterContentInset = [self.layouter contentInsetForPageViewController:self]; + } else { + self.layouterContentInset = UIEdgeInsetsZero; + } + + if([self.layouter respondsToSelector:@selector(interItemSpacingForPageViewController:)]) { + self.layouterInterItemSpacing = round([self.layouter interItemSpacingForPageViewController:self]); + } else { + self.layouterInterItemSpacing = 0.0f; + } + + CGRect frame = [self.layouter finalFrameForPageAtIndex:self.numberOfPages - 1 pageViewController:self]; + if(self.layouter.navigationType == SCPageLayouterNavigationTypeVertical) { + [self.scrollView setContentInset:UIEdgeInsetsMake(self.layouterContentInset.top, 0.0f, self.layouterContentInset.bottom, 0.0f)]; + [self.scrollView setContentSize:CGSizeMake(0.0f, round(MAX(CGRectGetHeight(self.scrollView.bounds), CGRectGetMaxY(frame))))]; + } else { + [self.scrollView setContentInset:UIEdgeInsetsMake(0.0f, self.layouterContentInset.left, 0.0f, self.layouterContentInset.right)]; + [self.scrollView setContentSize:CGSizeMake(round(MAX(CGRectGetWidth(self.scrollView.bounds), CGRectGetMaxX(frame))), 0.0f)]; + } + + [self _updateNavigationContraints]; } - (void)_updateNavigationContraints { - if(self.continuousNavigationEnabled) { - return; - } - - if(self.layouter.navigationConstraintType == SCPageLayouterNavigationContraintTypeNone) { - return; - } - - UIEdgeInsets insets = UIEdgeInsetsZero; - - CGRect frame = [self.layouter finalFrameForPageAtIndex:(self.currentPage == 0 ? 0 : self.currentPage - 1) pageViewController:self]; - switch (self.layouter.navigationType) { - case SCPageLayouterNavigationTypeVertical: - frame.origin.x = 0.0f; - frame.origin.y -= self.layouterContentInset.top; - break; - case SCPageLayouterNavigationTypeHorizontal: - frame.origin.y = 0.0f; - frame.origin.x -= self.layouterContentInset.left; - break; - default: - break; - } - - if(self.layouter.navigationConstraintType & SCPageLayouterNavigationContraintTypeReverse) { - switch (self.layouter.navigationType) { - case SCPageLayouterNavigationTypeVertical: { - insets.top = -[self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, -1.0f)].y; - break; - } - case SCPageLayouterNavigationTypeHorizontal: { - insets.left = -[self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(-1.0f, 0.0f)].x; - break; - } - } - } - - frame = [self.layouter finalFrameForPageAtIndex:MIN(self.currentPage + 1, self.numberOfPages - 1) pageViewController:self]; - switch (self.layouter.navigationType) { - case SCPageLayouterNavigationTypeVertical: - frame.origin.x = 0.0f; - frame.origin.y += self.layouterContentInset.top; - break; - case SCPageLayouterNavigationTypeHorizontal: - frame.origin.y = 0.0f; - frame.origin.x += self.layouterContentInset.left; - break; - default: - break; - } - - if(self.layouter.navigationConstraintType & SCPageLayouterNavigationContraintTypeForward) { - switch (self.layouter.navigationType) { - case SCPageLayouterNavigationTypeVertical: { - insets.bottom = -(self.scrollView.contentSize.height - ABS([self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, 1.0f)].y)); - break; - } - case SCPageLayouterNavigationTypeHorizontal: { - insets.right = -(self.scrollView.contentSize.width - ABS([self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(1.0f, 0.0f)].x)); - break; - } - } - } - - [self.scrollView setContentInset:insets]; + if(self.continuousNavigationEnabled) { + return; + } + + if(self.layouter.navigationConstraintType == SCPageLayouterNavigationContraintTypeNone) { + return; + } + + UIEdgeInsets insets = UIEdgeInsetsZero; + + CGRect frame = [self.layouter finalFrameForPageAtIndex:(self.currentPage == 0 ? 0 : self.currentPage - 1) pageViewController:self]; + switch (self.layouter.navigationType) { + case SCPageLayouterNavigationTypeVertical: + frame.origin.x = 0.0f; + frame.origin.y -= self.layouterContentInset.top; + break; + case SCPageLayouterNavigationTypeHorizontal: + frame.origin.y = 0.0f; + frame.origin.x -= self.layouterContentInset.left; + break; + default: + break; + } + + if(self.layouter.navigationConstraintType & SCPageLayouterNavigationContraintTypeReverse) { + switch (self.layouter.navigationType) { + case SCPageLayouterNavigationTypeVertical: { + insets.top = -[self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, -1.0f)].y; + break; + } + case SCPageLayouterNavigationTypeHorizontal: { + insets.left = -[self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(-1.0f, 0.0f)].x; + break; + } + } + } + + frame = [self.layouter finalFrameForPageAtIndex:MIN(self.currentPage + 1, self.numberOfPages - 1) pageViewController:self]; + switch (self.layouter.navigationType) { + case SCPageLayouterNavigationTypeVertical: + frame.origin.x = 0.0f; + frame.origin.y += self.layouterContentInset.top; + break; + case SCPageLayouterNavigationTypeHorizontal: + frame.origin.y = 0.0f; + frame.origin.x += self.layouterContentInset.left; + break; + default: + break; + } + + if(self.layouter.navigationConstraintType & SCPageLayouterNavigationContraintTypeForward) { + switch (self.layouter.navigationType) { + case SCPageLayouterNavigationTypeVertical: { + insets.bottom = -(self.scrollView.contentSize.height - ABS([self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, 1.0f)].y)); + break; + } + case SCPageLayouterNavigationTypeHorizontal: { + insets.right = -(self.scrollView.contentSize.width - ABS([self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(1.0f, 0.0f)].x)); + break; + } + } + } + + [self.scrollView setContentInset:insets]; } #pragma mark - Page Management - (void)_tilePages { - if(self.numberOfPages == 0) { - return; - } - + if(self.numberOfPages == 0) { + return; + } + if(!self.initialPageIndex) { self.currentPage = [self _calculateCurrentPage]; } - - NSInteger firstNeededPageIndex = self.currentPage - [self.layouter numberOfPagesToPreloadBeforeCurrentPage]; - firstNeededPageIndex = MAX(firstNeededPageIndex, 0); - - - NSInteger lastNeededPageIndex = self.currentPage + [self.layouter numberOfPagesToPreloadAfterCurrentPage]; - lastNeededPageIndex = MIN(lastNeededPageIndex, ((NSInteger)self.numberOfPages - 1)); - - NSMutableSet *removedIndexes = [NSMutableSet set]; - - [self.pages enumerateObjectsUsingBlock:^(SCPageViewControllerPageDetails *pageDetails, NSUInteger pageIndex, BOOL *stop) { - - if([pageDetails isEqual:[NSNull null]]) { - return; - } - - if (pageIndex < firstNeededPageIndex || pageIndex > lastNeededPageIndex) { - [removedIndexes addObject:@(pageIndex)]; - [self _removePageAtIndex:pageIndex]; - } - }]; - - for(NSNumber *removedIndex in removedIndexes) { - [self.pages replaceObjectAtIndex:removedIndex.unsignedIntegerValue withObject:[NSNull null]]; - } - - for (NSUInteger pageIndex = firstNeededPageIndex; pageIndex <= lastNeededPageIndex; pageIndex++) { - - if([self.insertionIndexes containsIndex:pageIndex]) { - continue; - } - - UIViewController *page = [self viewControllerForPageAtIndex:pageIndex]; - if (!page) { - [self _createAndInsertNewPageAtIndex:pageIndex]; - } - } - - [self _updateFramesAndTriggerAppearanceCallbacks]; + + NSInteger firstNeededPageIndex = self.currentPage - [self.layouter numberOfPagesToPreloadBeforeCurrentPage]; + firstNeededPageIndex = MAX(firstNeededPageIndex, 0); + + + NSInteger lastNeededPageIndex = self.currentPage + [self.layouter numberOfPagesToPreloadAfterCurrentPage]; + lastNeededPageIndex = MIN(lastNeededPageIndex, ((NSInteger)self.numberOfPages - 1)); + + NSMutableSet *removedIndexes = [NSMutableSet set]; + + [self.pages enumerateObjectsUsingBlock:^(SCPageViewControllerPageDetails *pageDetails, NSUInteger pageIndex, BOOL *stop) { + + if([pageDetails isEqual:[NSNull null]]) { + return; + } + + if (pageIndex < firstNeededPageIndex || pageIndex > lastNeededPageIndex) { + [removedIndexes addObject:@(pageIndex)]; + [self _removePageAtIndex:pageIndex]; + } + }]; + + for(NSNumber *removedIndex in removedIndexes) { + [self.pages replaceObjectAtIndex:removedIndex.unsignedIntegerValue withObject:[NSNull null]]; + } + + for (NSUInteger pageIndex = firstNeededPageIndex; pageIndex <= lastNeededPageIndex; pageIndex++) { + + if([self.insertionIndexes containsIndex:pageIndex]) { + continue; + } + + UIViewController *page = [self viewControllerForPageAtIndex:pageIndex]; + if (!page) { + [self _createAndInsertNewPageAtIndex:pageIndex]; + } + } + + [self _updateFramesAndTriggerAppearanceCallbacks]; } #pragma mark Appearance callbacks and framesetting - (void)_updateFramesAndTriggerAppearanceCallbacks { - NSArray *filteredPages = [self.pages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != %@", [NSNull null]]]; - - if(filteredPages.count == 0) { - return; - } - - NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"viewController.view" ascending:NO comparator:^NSComparisonResult(id obj1, id obj2) { - return [@([self.scrollView.subviews indexOfObject:obj1]) compare:@([self.scrollView.subviews indexOfObject:obj2])]; - }]; - - NSArray *sortedPages = [filteredPages sortedArrayUsingDescriptors:@[sortDescriptor]]; - - BOOL isReversed = (![filteredPages isEqual:sortedPages]); - - __block CGRect remainder = self.scrollView.bounds; - if(remainder.origin.x < 0.0f || remainder.origin.y < 0.0f) { - remainder.size.width += remainder.origin.x; - remainder.size.height += remainder.origin.y; - remainder.origin.x = 0.0f; - remainder.origin.y = 0.0f; - } - - CGRectEdge edge = -1; - SCPageViewControllerPageDetails *firstPage = [sortedPages firstObject]; - switch (self.layouter.navigationType) { - case SCPageLayouterNavigationTypeVertical: { - if(isReversed) { - edge = CGRectMaxYEdge; - - CGFloat remainderDelta = CGRectGetMaxY(remainder) - CGRectGetMaxY(firstPage.viewController.view.frame); - if(remainderDelta > 0) { - remainder.size.height -= remainderDelta; - } - - } else { - edge = CGRectMinYEdge; - } - - break; - } - case SCPageLayouterNavigationTypeHorizontal: { - if(isReversed) { - edge = CGRectMaxXEdge; - - CGFloat remainderDelta = CGRectGetMaxX(remainder) - CGRectGetMaxX(firstPage.viewController.view.frame); - if(remainderDelta > 0) { - remainder.size.height -= remainderDelta; - } - - } else { - edge = CGRectMinXEdge; - } - - break; - } - } - - [sortedPages enumerateObjectsUsingBlock:^(SCPageViewControllerPageDetails *details, NSUInteger idx, BOOL *stop) { - - UIViewController *viewController = details.viewController; - - if(!viewController) { - return; - } - - NSUInteger pageIndex = [self.pages indexOfObject:details]; - - CGRect finalFrame = [self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]; - CGRect nextFrame = finalFrame; - if([self.layouter respondsToSelector:@selector(currentFrameForPageAtIndex:contentOffset:finalFrame:pageViewController:)]) { - nextFrame = [self.layouter currentFrameForPageAtIndex:pageIndex - contentOffset:self.scrollView.contentOffset - finalFrame:finalFrame - pageViewController:self]; - } - - CGRect intersection = CGRectIntersection(remainder, nextFrame); - // If a view controller's frame does intersect the remainder then it's visible - BOOL visible = self.layouter.navigationType == SCPageLayouterNavigationTypeVertical ? (CGRectGetHeight(intersection) > 0.0f) : (CGRectGetWidth(intersection) > 0.0f); - visible = visible && self.isViewVisible; - - if(visible) { - if(self.layouter.navigationType == SCPageLayouterNavigationTypeVertical) { - [details setVisiblePercentage:round((CGRectGetHeight(intersection) * 1000) / CGRectGetHeight(nextFrame)) / 1000.0f]; - } else { - [details setVisiblePercentage:round((CGRectGetWidth(intersection) * 1000) / CGRectGetWidth(nextFrame)) / 1000.0f]; - } - } - - remainder = [self _subtractRect:intersection fromRect:remainder withEdge:edge]; + NSArray *filteredPages = [self.pages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != %@", [NSNull null]]]; + + if(filteredPages.count == 0) { + return; + } + + NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"viewController.view" ascending:NO comparator:^NSComparisonResult(id obj1, id obj2) { + return [@([self.scrollView.subviews indexOfObject:obj1]) compare:@([self.scrollView.subviews indexOfObject:obj2])]; + }]; + + NSArray *sortedPages = [filteredPages sortedArrayUsingDescriptors:@[sortDescriptor]]; + + BOOL isReversed = (![filteredPages isEqual:sortedPages]); + + __block CGRect remainder = self.scrollView.bounds; + if(remainder.origin.x < 0.0f || remainder.origin.y < 0.0f) { + remainder.size.width += remainder.origin.x; + remainder.size.height += remainder.origin.y; + remainder.origin.x = 0.0f; + remainder.origin.y = 0.0f; + } + + CGRectEdge edge = -1; + SCPageViewControllerPageDetails *firstPage = [sortedPages firstObject]; + switch (self.layouter.navigationType) { + case SCPageLayouterNavigationTypeVertical: { + if(isReversed) { + edge = CGRectMaxYEdge; + + CGFloat remainderDelta = CGRectGetMaxY(remainder) - CGRectGetMaxY(firstPage.viewController.view.frame); + if(remainderDelta > 0) { + remainder.size.height -= remainderDelta; + } + + } else { + edge = CGRectMinYEdge; + } + + break; + } + case SCPageLayouterNavigationTypeHorizontal: { + if(isReversed) { + edge = CGRectMaxXEdge; + + CGFloat remainderDelta = CGRectGetMaxX(remainder) - CGRectGetMaxX(firstPage.viewController.view.frame); + if(remainderDelta > 0) { + remainder.size.height -= remainderDelta; + } + + } else { + edge = CGRectMinXEdge; + } + + break; + } + } + + [sortedPages enumerateObjectsUsingBlock:^(SCPageViewControllerPageDetails *details, NSUInteger idx, BOOL *stop) { + + UIViewController *viewController = details.viewController; + + if(!viewController) { + return; + } + + NSUInteger pageIndex = [self.pages indexOfObject:details]; + + CGRect finalFrame = [self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]; + CGRect nextFrame = finalFrame; + if([self.layouter respondsToSelector:@selector(currentFrameForPageAtIndex:contentOffset:finalFrame:pageViewController:)]) { + nextFrame = [self.layouter currentFrameForPageAtIndex:pageIndex + contentOffset:self.scrollView.contentOffset + finalFrame:finalFrame + pageViewController:self]; + } + + CGRect intersection = CGRectIntersection(remainder, nextFrame); + // If a view controller's frame does intersect the remainder then it's visible + BOOL visible = self.layouter.navigationType == SCPageLayouterNavigationTypeVertical ? (CGRectGetHeight(intersection) > 0.0f) : (CGRectGetWidth(intersection) > 0.0f); + visible = visible && self.isViewVisible; + + if(visible) { + if(self.layouter.navigationType == SCPageLayouterNavigationTypeVertical) { + [details setVisiblePercentage:round((CGRectGetHeight(intersection) * 1000) / CGRectGetHeight(nextFrame)) / 1000.0f]; + } else { + [details setVisiblePercentage:round((CGRectGetWidth(intersection) * 1000) / CGRectGetWidth(nextFrame)) / 1000.0f]; + } + } + + remainder = [self _subtractRect:intersection fromRect:remainder withEdge:edge]; CATransform3D previousTransform = viewController.view.layer.transform; [self _setAnimatableSublayerTransform:CATransform3DIdentity forViewController:viewController]; - - // Finally, trigger appearance callbacks and new frame - if(visible && ![self.visibleControllers containsObject:viewController]) { - [self.visibleControllers addObject:viewController]; - [viewController beginAppearanceTransition:YES animated:NO]; - [viewController.view setFrame:nextFrame]; - [viewController endAppearanceTransition]; - - if([self.delegate respondsToSelector:@selector(pageViewController:didShowViewController:atIndex:)]) { - [self.delegate pageViewController:self didShowViewController:viewController atIndex:pageIndex]; - } - - } else if(!visible && [self.visibleControllers containsObject:viewController]) { - [self.visibleControllers removeObject:viewController]; - [viewController beginAppearanceTransition:NO animated:NO]; - [viewController.view setFrame:nextFrame]; - [viewController endAppearanceTransition]; - - if([self.delegate respondsToSelector:@selector(pageViewController:didHideViewController:atIndex:)]) { - [self.delegate pageViewController:self didHideViewController:viewController atIndex:pageIndex]; - } - - } else { - [viewController.view setFrame:nextFrame]; - } - + + // Finally, trigger appearance callbacks and new frame + if(visible && ![self.visibleControllers containsObject:viewController]) { + [self.visibleControllers addObject:viewController]; + [viewController beginAppearanceTransition:YES animated:NO]; + [viewController.view setFrame:nextFrame]; + [viewController endAppearanceTransition]; + + if([self.delegate respondsToSelector:@selector(pageViewController:didShowViewController:atIndex:)]) { + [self.delegate pageViewController:self didShowViewController:viewController atIndex:pageIndex]; + } + + } else if(!visible && [self.visibleControllers containsObject:viewController]) { + [self.visibleControllers removeObject:viewController]; + [viewController beginAppearanceTransition:NO animated:NO]; + [viewController.view setFrame:nextFrame]; + [viewController endAppearanceTransition]; + + if([self.delegate respondsToSelector:@selector(pageViewController:didHideViewController:atIndex:)]) { + [self.delegate pageViewController:self didHideViewController:viewController atIndex:pageIndex]; + } + + } else { + [viewController.view setFrame:nextFrame]; + } + if([self.layouter respondsToSelector:@selector(sublayerTransformForPageAtIndex:contentOffset:pageViewController:)]) { CATransform3D transform = [self.layouter sublayerTransformForPageAtIndex:pageIndex contentOffset:self.scrollView.contentOffset @@ -653,410 +653,410 @@ - (void)_updateFramesAndTriggerAppearanceCallbacks - (BOOL)shouldAutomaticallyForwardAppearanceMethods { - return NO; + return NO; } #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - if([self.layouter respondsToSelector:@selector(pageViewController:didNavigateToOffset:)]) { - [self.layouter pageViewController:self didNavigateToOffset:self.scrollView.contentOffset]; - } - - if([self.delegate respondsToSelector:@selector(pageViewController:didNavigateToOffset:)]) { - [self.delegate pageViewController:self didNavigateToOffset:self.scrollView.contentOffset]; - } - - if(!self.shouldLayoutPagesOnRest) { - [self _tilePages]; - } + if([self.layouter respondsToSelector:@selector(pageViewController:didNavigateToOffset:)]) { + [self.layouter pageViewController:self didNavigateToOffset:self.scrollView.contentOffset]; + } + + if([self.delegate respondsToSelector:@selector(pageViewController:didNavigateToOffset:)]) { + [self.delegate pageViewController:self didNavigateToOffset:self.scrollView.contentOffset]; + } + + if(!self.shouldLayoutPagesOnRest) { + [self _tilePages]; + } } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { - if(decelerate == NO) { - - if(self.shouldLayoutPagesOnRest) { - [self _tilePages]; - } - - [self _updateNavigationContraints]; - - if([self.delegate respondsToSelector:@selector(pageViewController:didNavigateToPageAtIndex:)]) { - [self.delegate pageViewController:self didNavigateToPageAtIndex:self.currentPage]; - } - } + if(decelerate == NO) { + + if(self.shouldLayoutPagesOnRest) { + [self _tilePages]; + } + + [self _updateNavigationContraints]; + + if([self.delegate respondsToSelector:@selector(pageViewController:didNavigateToPageAtIndex:)]) { + [self.delegate pageViewController:self didNavigateToPageAtIndex:self.currentPage]; + } + } } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - if(self.shouldLayoutPagesOnRest) { - [self _tilePages]; - } - - [self _updateNavigationContraints]; - - if([self.delegate respondsToSelector:@selector(pageViewController:didNavigateToPageAtIndex:)]) { - [self.delegate pageViewController:self didNavigateToPageAtIndex:self.currentPage]; - } + if(self.shouldLayoutPagesOnRest) { + [self _tilePages]; + } + + [self _updateNavigationContraints]; + + if([self.delegate respondsToSelector:@selector(pageViewController:didNavigateToPageAtIndex:)]) { + [self.delegate pageViewController:self didNavigateToPageAtIndex:self.currentPage]; + } } - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { - if(self.shouldLayoutPagesOnRest) { - [self _tilePages]; - } - - [self _updateNavigationContraints]; - - if([self.delegate respondsToSelector:@selector(pageViewController:didNavigateToPageAtIndex:)]) { - [self.delegate pageViewController:self didNavigateToPageAtIndex:self.currentPage]; - } + if(self.shouldLayoutPagesOnRest) { + [self _tilePages]; + } + + [self _updateNavigationContraints]; + + if([self.delegate respondsToSelector:@selector(pageViewController:didNavigateToPageAtIndex:)]) { + [self.delegate pageViewController:self didNavigateToPageAtIndex:self.currentPage]; + } } - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { - // Bouncing target content offset when fix. - // When trying to adjust content offset while bouncing the velocity drops down to almost nothing. - // Seems to be an internal UIScrollView issue - if(self.scrollView.contentOffset.y < -self.layouterContentInset.top) { - targetContentOffset->y = -self.layouterContentInset.top; - } else if(self.scrollView.contentOffset.x < -self.layouterContentInset.left) { - targetContentOffset->x = -self.layouterContentInset.left; - } else if(self.scrollView.contentOffset.y > ABS((self.scrollView.contentSize.height + self.layouterContentInset.bottom) - CGRectGetHeight(self.scrollView.bounds))) { - targetContentOffset->y = (self.scrollView.contentSize.height + self.layouterContentInset.bottom) - CGRectGetHeight(self.scrollView.bounds); - } else if(self.scrollView.contentOffset.x > ABS((self.scrollView.contentSize.width + self.layouterContentInset.right) - CGRectGetWidth(self.scrollView.bounds))) { - targetContentOffset->x = (self.scrollView.contentSize.width + self.layouterContentInset.right) - CGRectGetWidth(self.scrollView.bounds); - } - // Normal pagination - else { - [self _adjustTargetContentOffset:targetContentOffset withVelocity:velocity]; - } + // Bouncing target content offset when fix. + // When trying to adjust content offset while bouncing the velocity drops down to almost nothing. + // Seems to be an internal UIScrollView issue + if(self.scrollView.contentOffset.y < -self.layouterContentInset.top) { + targetContentOffset->y = -self.layouterContentInset.top; + } else if(self.scrollView.contentOffset.x < -self.layouterContentInset.left) { + targetContentOffset->x = -self.layouterContentInset.left; + } else if(self.scrollView.contentOffset.y > ABS((self.scrollView.contentSize.height + self.layouterContentInset.bottom) - CGRectGetHeight(self.scrollView.bounds))) { + targetContentOffset->y = (self.scrollView.contentSize.height + self.layouterContentInset.bottom) - CGRectGetHeight(self.scrollView.bounds); + } else if(self.scrollView.contentOffset.x > ABS((self.scrollView.contentSize.width + self.layouterContentInset.right) - CGRectGetWidth(self.scrollView.bounds))) { + targetContentOffset->x = (self.scrollView.contentSize.width + self.layouterContentInset.right) - CGRectGetWidth(self.scrollView.bounds); + } + // Normal pagination + else { + [self _adjustTargetContentOffset:targetContentOffset withVelocity:velocity]; + } } #pragma mark - SCPageViewControllerViewDelegate - (void)pageViewControllerViewWillChangeFrame:(SCPageViewControllerView *)pageViewControllerView { - [self.scrollView setDelegate:nil]; + [self.scrollView setDelegate:nil]; } - (void)pageViewControllerViewDidChangeFrame:(SCPageViewControllerView *)pageViewControllerView { - [self.scrollView setDelegate:self]; + [self.scrollView setDelegate:self]; } #pragma mark - Private - Content Offset Blocking - (void)_blockContentOffsetOnPageAtIndex:(NSUInteger)pageIndex { - self.blockedPageIndex = pageIndex; - - if(!self.isContentOffsetBlocked) { - self.isContentOffsetBlocked = YES; - [self.scrollView setDelegate:nil]; - [self.scrollView addObserver:self forKeyPath:@"contentOffset" options:0 context:nil]; - } + self.blockedPageIndex = pageIndex; + + if(!self.isContentOffsetBlocked) { + self.isContentOffsetBlocked = YES; + [self.scrollView setDelegate:nil]; + [self.scrollView addObserver:self forKeyPath:@"contentOffset" options:0 context:nil]; + } } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if(self.isContentOffsetBlocked) { - [self _centerOnPageIndex:self.blockedPageIndex]; - } + if(self.isContentOffsetBlocked) { + [self _centerOnPageIndex:self.blockedPageIndex]; + } } - (void)_unblockContentOffset { - if(self.isContentOffsetBlocked) { - [self.scrollView removeObserver:self forKeyPath:@"contentOffset"]; - self.isContentOffsetBlocked = NO; - [self.scrollView setDelegate:self]; - } + if(self.isContentOffsetBlocked) { + [self.scrollView removeObserver:self forKeyPath:@"contentOffset"]; + self.isContentOffsetBlocked = NO; + [self.scrollView setDelegate:self]; + } } #pragma mark Private - Pagination - (NSUInteger)_calculateCurrentPage { - NSMutableArray *pages = [self.pages mutableCopy]; - for(NSUInteger i = 0; i < pages.count; i++) { - SCPageViewControllerPageDetails *details = pages[i]; - - if([details isEqual:[NSNull null]]) { - SCPageViewControllerPageDetails *newDetails = [[SCPageViewControllerPageDetails alloc] init]; - NSUInteger zPosition = pages.count - i - 1; - if([self.layouter respondsToSelector:@selector(zPositionForPageAtIndex:pageViewController:)]) { - zPosition = [self.layouter zPositionForPageAtIndex:i pageViewController:self]; - } - - [newDetails setZPosition:zPosition]; - [pages replaceObjectAtIndex:i withObject:newDetails]; - } - } - - NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"zPosition" ascending:NO]; - NSArray *sortedPages = [pages sortedArrayUsingDescriptors:@[sortDescriptor]]; - - for(NSUInteger i = 0; i < sortedPages.count; i++) { - - NSUInteger pageIndex = [pages indexOfObject:sortedPages[i]]; - - CGRect frame = [self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]; - + NSMutableArray *pages = [self.pages mutableCopy]; + for(NSUInteger i = 0; i < pages.count; i++) { + SCPageViewControllerPageDetails *details = pages[i]; + + if([details isEqual:[NSNull null]]) { + SCPageViewControllerPageDetails *newDetails = [[SCPageViewControllerPageDetails alloc] init]; + NSUInteger zPosition = pages.count - i - 1; + if([self.layouter respondsToSelector:@selector(zPositionForPageAtIndex:pageViewController:)]) { + zPosition = [self.layouter zPositionForPageAtIndex:i pageViewController:self]; + } + + [newDetails setZPosition:zPosition]; + [pages replaceObjectAtIndex:i withObject:newDetails]; + } + } + + NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"zPosition" ascending:NO]; + NSArray *sortedPages = [pages sortedArrayUsingDescriptors:@[sortDescriptor]]; + + for(NSUInteger i = 0; i < sortedPages.count; i++) { + + NSUInteger pageIndex = [pages indexOfObject:sortedPages[i]]; + + CGRect frame = [self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]; + CGPoint centerOffset = self.scrollView.contentOffset; centerOffset.x += CGRectGetWidth(self.scrollView.bounds) / 2.0f; centerOffset.y += CGRectGetHeight(self.scrollView.bounds) / 2.0f; - - if(CGRectContainsPoint(frame, centerOffset)) { - return pageIndex; - } - } - - return self.currentPage; + + if(CGRectContainsPoint(frame, centerOffset)) { + return pageIndex; + } + } + + return self.currentPage; } - (CGPoint)_nextStepOffsetForFrame:(CGRect)finalFrame withVelocity:(CGPoint)velocity { - CGPoint nextStepOffset = CGPointZero; - if(velocity.y > 0.0f) { - nextStepOffset.y = (NSInteger)CGRectGetMaxY(finalFrame); - } else if(velocity.x > 0.0f) { - nextStepOffset.x = (NSInteger)CGRectGetMaxX(finalFrame); - } else if(velocity.y < 0.0f) { + CGPoint nextStepOffset = CGPointZero; + if(velocity.y > 0.0f) { + nextStepOffset.y = (NSInteger)CGRectGetMaxY(finalFrame); + } else if(velocity.x > 0.0f) { + nextStepOffset.x = (NSInteger)CGRectGetMaxX(finalFrame); + } else if(velocity.y < 0.0f) { nextStepOffset.y = (NSInteger)CGRectGetMinY(finalFrame); - } else if(velocity.x < 0.0f) { - nextStepOffset.x = (NSInteger)CGRectGetMinX(finalFrame); - } - - return nextStepOffset; + } else if(velocity.x < 0.0f) { + nextStepOffset.x = (NSInteger)CGRectGetMinX(finalFrame); + } + + return nextStepOffset; } - (void)_adjustTargetContentOffset:(inout CGPoint *)targetContentOffset withVelocity:(CGPoint)velocity { - if(!self.pagingEnabled && self.continuousNavigationEnabled) { - return; - } - - CGPoint adjustedOffset = *targetContentOffset; - if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { - adjustedOffset.x += self.layouterContentInset.left; - } - else { - adjustedOffset.y += self.layouterContentInset.top; - } - - // Enumerate through all the pages and figure out which one contains the targeted offset - for(NSUInteger pageIndex = 0; pageIndex < self.numberOfPages; pageIndex++) { - - CGRect frame = [self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]; - - CGRect adjustedFrame = frame; - if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { - adjustedFrame.origin.x -= self.layouterInterItemSpacing / 2.0f; - adjustedFrame.size.width += self.layouterInterItemSpacing; - adjustedFrame.origin.y = 0.0f; - adjustedFrame.size.height = self.scrollView.bounds.size.height; - } - else { - adjustedFrame.origin.y -= self.layouterInterItemSpacing / 2.0f; - adjustedFrame.size.height += self.layouterInterItemSpacing; - adjustedFrame.origin.x = 0.0f; - adjustedFrame.size.width = self.scrollView.bounds.size.width; - } - - if(CGRectContainsPoint(adjustedFrame, adjustedOffset)) { - - // Jump to the closest navigation step if the velocity is zero - if(CGPointEqualToPoint(CGPointZero, velocity)) { - if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { - CGPoint previousStepOffset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(-1.0f, 0.0f)]; - CGPoint nextStepOffset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(1.0f, 0.0f)]; - - if(ABS(adjustedOffset.x - previousStepOffset.x) > ABS(adjustedOffset.x - nextStepOffset.x)) { - adjustedOffset = nextStepOffset; - adjustedOffset.x += self.layouterInterItemSpacing; - } else { - adjustedOffset = previousStepOffset; - } - } else { - CGPoint previousStepOffset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, -1.0f)]; - CGPoint nextStepOffset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, 1.0f)]; - - if(ABS(adjustedOffset.y - previousStepOffset.y) > ABS(adjustedOffset.y - nextStepOffset.y)) { - adjustedOffset = nextStepOffset; - adjustedOffset.y += self.layouterInterItemSpacing; - } else { - adjustedOffset = previousStepOffset; - } - } - } else { // Calculate the next step of the pagination (either a navigationStep or a controller edge) - adjustedOffset = [self _nextStepOffsetForFrame:frame withVelocity:velocity]; - if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { - if(velocity.x > 0) { - adjustedOffset.x += self.layouterInterItemSpacing; - } - } else { - if(velocity.y > 0) { - adjustedOffset.y += self.layouterInterItemSpacing; - } - } - } - - break; - } - } - - if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { - adjustedOffset.y = 0.0f; - adjustedOffset.x -= self.layouterContentInset.left; - } else { - adjustedOffset.x = 0.0f; - adjustedOffset.y -= self.layouterContentInset.top; - } - - *targetContentOffset = adjustedOffset; + if(!self.pagingEnabled && self.continuousNavigationEnabled) { + return; + } + + CGPoint adjustedOffset = *targetContentOffset; + if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { + adjustedOffset.x += self.layouterContentInset.left; + } + else { + adjustedOffset.y += self.layouterContentInset.top; + } + + // Enumerate through all the pages and figure out which one contains the targeted offset + for(NSUInteger pageIndex = 0; pageIndex < self.numberOfPages; pageIndex++) { + + CGRect frame = [self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]; + + CGRect adjustedFrame = frame; + if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { + adjustedFrame.origin.x -= self.layouterInterItemSpacing / 2.0f; + adjustedFrame.size.width += self.layouterInterItemSpacing; + adjustedFrame.origin.y = 0.0f; + adjustedFrame.size.height = self.scrollView.bounds.size.height; + } + else { + adjustedFrame.origin.y -= self.layouterInterItemSpacing / 2.0f; + adjustedFrame.size.height += self.layouterInterItemSpacing; + adjustedFrame.origin.x = 0.0f; + adjustedFrame.size.width = self.scrollView.bounds.size.width; + } + + if(CGRectContainsPoint(adjustedFrame, adjustedOffset)) { + + // Jump to the closest navigation step if the velocity is zero + if(CGPointEqualToPoint(CGPointZero, velocity)) { + if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { + CGPoint previousStepOffset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(-1.0f, 0.0f)]; + CGPoint nextStepOffset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(1.0f, 0.0f)]; + + if(ABS(adjustedOffset.x - previousStepOffset.x) > ABS(adjustedOffset.x - nextStepOffset.x)) { + adjustedOffset = nextStepOffset; + adjustedOffset.x += self.layouterInterItemSpacing; + } else { + adjustedOffset = previousStepOffset; + } + } else { + CGPoint previousStepOffset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, -1.0f)]; + CGPoint nextStepOffset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, 1.0f)]; + + if(ABS(adjustedOffset.y - previousStepOffset.y) > ABS(adjustedOffset.y - nextStepOffset.y)) { + adjustedOffset = nextStepOffset; + adjustedOffset.y += self.layouterInterItemSpacing; + } else { + adjustedOffset = previousStepOffset; + } + } + } else { // Calculate the next step of the pagination (either a navigationStep or a controller edge) + adjustedOffset = [self _nextStepOffsetForFrame:frame withVelocity:velocity]; + if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { + if(velocity.x > 0) { + adjustedOffset.x += self.layouterInterItemSpacing; + } + } else { + if(velocity.y > 0) { + adjustedOffset.y += self.layouterInterItemSpacing; + } + } + } + + break; + } + } + + if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { + adjustedOffset.y = 0.0f; + adjustedOffset.x -= self.layouterContentInset.left; + } else { + adjustedOffset.x = 0.0f; + adjustedOffset.y -= self.layouterContentInset.top; + } + + *targetContentOffset = adjustedOffset; } #pragma mark - Private - (CGRect)_subtractRect:(CGRect)r2 fromRect:(CGRect)r1 withEdge:(CGRectEdge)edge { - CGRect intersection = CGRectIntersection(r1, r2); - if (CGRectIsNull(intersection)) { - return r1; - } - - CGFloat chopAmount = (edge == CGRectMinXEdge || edge == CGRectMaxXEdge) ? CGRectGetWidth(intersection) : CGRectGetHeight(intersection); - - CGRect remainder, throwaway; - CGRectDivide(r1, &throwaway, &remainder, chopAmount, edge); - return remainder; + CGRect intersection = CGRectIntersection(r1, r2); + if (CGRectIsNull(intersection)) { + return r1; + } + + CGFloat chopAmount = (edge == CGRectMinXEdge || edge == CGRectMaxXEdge) ? CGRectGetWidth(intersection) : CGRectGetHeight(intersection); + + CGRect remainder, throwaway; + CGRectDivide(r1, &throwaway, &remainder, chopAmount, edge); + return remainder; } - (void)_setAnimatableSublayerTransform:(CATransform3D)transform forViewController:(UIViewController *)viewController { - for(CALayer *layer in viewController.view.layer.sublayers) { - [layer setTransform:transform]; - } + for(CALayer *layer in viewController.view.layer.sublayers) { + [layer setTransform:transform]; + } } - (void)_centerOnPageIndex:(NSUInteger)pageIndex { - CGRect frame = CGRectIntegral([self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]); - - CGPoint offset; - if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { - offset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(-1.0f, 0.0f)]; - offset.x -= self.layouterContentInset.left; - } else { - offset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, -1.0f)]; - offset.y -= self.layouterContentInset.top; - } - - offset = CGPointMake((NSInteger)floor(offset.x), (NSInteger)floor(offset.y)); - - if(offset.x != (NSInteger)floor(self.scrollView.contentOffset.x) || - offset.y != (NSInteger)floor(self.scrollView.contentOffset.y)) { - [self.scrollView setContentOffset:offset]; - } + CGRect frame = CGRectIntegral([self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]); + + CGPoint offset; + if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { + offset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(-1.0f, 0.0f)]; + offset.x -= self.layouterContentInset.left; + } else { + offset = [self _nextStepOffsetForFrame:frame withVelocity:CGPointMake(0.0f, -1.0f)]; + offset.y -= self.layouterContentInset.top; + } + + offset = CGPointMake((NSInteger)floor(offset.x), (NSInteger)floor(offset.y)); + + if(offset.x != (NSInteger)floor(self.scrollView.contentOffset.x) || + offset.y != (NSInteger)floor(self.scrollView.contentOffset.y)) { + [self.scrollView setContentOffset:offset]; + } } - (void)_sortSubviewsByZPosition { - NSArray *filteredPages = [self.pages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != %@", [NSNull null]]]; - - [filteredPages enumerateObjectsUsingBlock:^(SCPageViewControllerPageDetails *pageDetails, NSUInteger pageIndex, BOOL *stop) { - NSUInteger zPosition = self.numberOfPages - [self.pages indexOfObject:pageDetails] - 1; - if([self.layouter respondsToSelector:@selector(zPositionForPageAtIndex:pageViewController:)]) { - zPosition = [self.layouter zPositionForPageAtIndex:[self.pages indexOfObject:pageDetails] - pageViewController:self]; - } - - NSAssert(zPosition < (NSInteger)self.numberOfPages, @"Invalid zPosition for page at index %lu", (unsigned long)pageIndex); - [pageDetails setZPosition:zPosition]; - }]; - - NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"zPosition" ascending:NO]; - NSMutableArray *sortedViews = [[[filteredPages sortedArrayUsingDescriptors:@[sortDescriptor]] valueForKeyPath:@"@unionOfObjects.viewController.view"] mutableCopy]; - - if(sortedViews.count != self.scrollView.subviews.count) { - // Keep no longer tracked views (pages being deleted for example) in the same hierarchical position - for(UIView *view in self.scrollView.subviews) { - if([sortedViews containsObject:view]) { - continue; - } - - NSUInteger index = [self.scrollView.subviews indexOfObject:view]; - if(index == 0) { - [sortedViews addObject:view]; - } else { - UIView *viewBelow = self.scrollView.subviews[index - 1]; - [sortedViews insertObject:view atIndex:[sortedViews indexOfObject:viewBelow]]; - } - } - } - - [sortedViews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { - [self.scrollView sendSubviewToBack:view]; - }]; + NSArray *filteredPages = [self.pages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != %@", [NSNull null]]]; + + [filteredPages enumerateObjectsUsingBlock:^(SCPageViewControllerPageDetails *pageDetails, NSUInteger pageIndex, BOOL *stop) { + NSUInteger zPosition = self.numberOfPages - [self.pages indexOfObject:pageDetails] - 1; + if([self.layouter respondsToSelector:@selector(zPositionForPageAtIndex:pageViewController:)]) { + zPosition = [self.layouter zPositionForPageAtIndex:[self.pages indexOfObject:pageDetails] + pageViewController:self]; + } + + NSAssert(zPosition < (NSInteger)self.numberOfPages, @"Invalid zPosition for page at index %lu", (unsigned long)pageIndex); + [pageDetails setZPosition:zPosition]; + }]; + + NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"zPosition" ascending:NO]; + NSMutableArray *sortedViews = [[[filteredPages sortedArrayUsingDescriptors:@[sortDescriptor]] valueForKeyPath:@"@unionOfObjects.viewController.view"] mutableCopy]; + + if(sortedViews.count != self.scrollView.subviews.count) { + // Keep no longer tracked views (pages being deleted for example) in the same hierarchical position + for(UIView *view in self.scrollView.subviews) { + if([sortedViews containsObject:view]) { + continue; + } + + NSUInteger index = [self.scrollView.subviews indexOfObject:view]; + if(index == 0) { + [sortedViews addObject:view]; + } else { + UIView *viewBelow = self.scrollView.subviews[index - 1]; + [sortedViews insertObject:view atIndex:[sortedViews indexOfObject:viewBelow]]; + } + } + } + + [sortedViews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { + [self.scrollView sendSubviewToBack:view]; + }]; } - (UIViewController *)_createAndInsertNewPageAtIndex:(NSUInteger)pageIndex { - SCPageViewControllerPageDetails *pageDetails = [self.pages objectAtIndex:pageIndex]; - if(![pageDetails isEqual:[NSNull null]] && pageDetails.viewController) { - return pageDetails.viewController; - } - - UIViewController *page = [self.dataSource pageViewController:self viewControllerForPageAtIndex:pageIndex]; - - SCPageViewControllerPageDetails *details = [[SCPageViewControllerPageDetails alloc] init]; - [details setViewController:page]; - [self.pages replaceObjectAtIndex:pageIndex withObject:details]; - - if(page) { - [self addChildViewController:page]; - } - - [page.view setAutoresizingMask:UIViewAutoresizingNone]; - [self.scrollView addSubview:page.view]; - - [UIView performWithoutAnimation:^{ - if(self.isAnimatingLayouterChange) { - [page.view setFrame:[self.previousLayouter finalFrameForPageAtIndex:pageIndex pageViewController:self]]; - } else { - [page.view setFrame:[self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]]; - } - }]; - - [self _sortSubviewsByZPosition]; - - [page didMoveToParentViewController:self]; - - return page; + SCPageViewControllerPageDetails *pageDetails = [self.pages objectAtIndex:pageIndex]; + if(![pageDetails isEqual:[NSNull null]] && pageDetails.viewController) { + return pageDetails.viewController; + } + + UIViewController *page = [self.dataSource pageViewController:self viewControllerForPageAtIndex:pageIndex]; + + SCPageViewControllerPageDetails *details = [[SCPageViewControllerPageDetails alloc] init]; + [details setViewController:page]; + [self.pages replaceObjectAtIndex:pageIndex withObject:details]; + + if(page) { + [self addChildViewController:page]; + } + + [page.view setAutoresizingMask:UIViewAutoresizingNone]; + [self.scrollView addSubview:page.view]; + + [UIView performWithoutAnimation:^{ + if(self.isAnimatingLayouterChange) { + [page.view setFrame:[self.previousLayouter finalFrameForPageAtIndex:pageIndex pageViewController:self]]; + } else { + [page.view setFrame:[self.layouter finalFrameForPageAtIndex:pageIndex pageViewController:self]]; + } + }]; + + [self _sortSubviewsByZPosition]; + + [page didMoveToParentViewController:self]; + + return page; } - (void)_removePageAtIndex:(NSUInteger)pageIndex { - SCPageViewControllerPageDetails *pageDetails = [self.pages objectAtIndex:pageIndex]; - if([pageDetails isEqual:[NSNull null]]) { - return; - } - - UIViewController *viewController = pageDetails.viewController; - if(!viewController) { - return; - } - - if([self.visibleControllers containsObject:viewController]) { - [viewController beginAppearanceTransition:NO animated:NO]; - } - - [viewController willMoveToParentViewController:nil]; - [viewController.view removeFromSuperview]; - [viewController removeFromParentViewController]; + SCPageViewControllerPageDetails *pageDetails = [self.pages objectAtIndex:pageIndex]; + if([pageDetails isEqual:[NSNull null]]) { + return; + } + + UIViewController *viewController = pageDetails.viewController; + if(!viewController) { + return; + } + + if([self.visibleControllers containsObject:viewController]) { + [viewController beginAppearanceTransition:NO animated:NO]; + } + + [viewController willMoveToParentViewController:nil]; + [viewController.view removeFromSuperview]; + [viewController removeFromParentViewController]; if([self.visibleControllers containsObject:viewController]) { [viewController endAppearanceTransition]; @@ -1073,364 +1073,364 @@ - (void)_removePageAtIndex:(NSUInteger)pageIndex - (void)reloadPagesAtIndexes:(NSIndexSet *)indexes animated:(BOOL)animated completion:(void(^)(void))completion { - NSMutableArray *removedViewControllers = [NSMutableArray array]; - - dispatch_group_t animationsDispatchGroup = dispatch_group_create(); - - [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger pageIndex, BOOL *stop) { - UIViewController *oldViewController = [self viewControllerForPageAtIndex:pageIndex]; + NSMutableArray *removedViewControllers = [NSMutableArray array]; + + dispatch_group_t animationsDispatchGroup = dispatch_group_create(); + + [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger pageIndex, BOOL *stop) { + UIViewController *oldViewController = [self viewControllerForPageAtIndex:pageIndex]; if(oldViewController) { [removedViewControllers addObject:oldViewController]; } - - [oldViewController willMoveToParentViewController:nil]; - if([self.visibleViewControllers containsObject:oldViewController]) { - [oldViewController beginAppearanceTransition:NO animated:animated]; - } - - [self.pages replaceObjectAtIndex:pageIndex withObject:[NSNull null]]; - UIViewController *newViewController = [self _createAndInsertNewPageAtIndex:pageIndex]; + + [oldViewController willMoveToParentViewController:nil]; + if([self.visibleViewControllers containsObject:oldViewController]) { + [oldViewController beginAppearanceTransition:NO animated:animated]; + } + + [self.pages replaceObjectAtIndex:pageIndex withObject:[NSNull null]]; + UIViewController *newViewController = [self _createAndInsertNewPageAtIndex:pageIndex]; if([newViewController isEqual:oldViewController]) { [removedViewControllers removeObject:newViewController]; } - - if(animated && [self.layouter respondsToSelector:@selector(animatePageReloadAtIndex:oldViewController:newViewController:pageViewController:completion:)]) { - dispatch_group_enter(animationsDispatchGroup); - [self.layouter animatePageReloadAtIndex:pageIndex oldViewController:oldViewController newViewController:newViewController pageViewController:self completion:^{ - dispatch_group_leave(animationsDispatchGroup); - }]; - } - }]; - - dispatch_group_notify(animationsDispatchGroup, dispatch_get_main_queue(), ^{ - - for(UIViewController *viewController in removedViewControllers) { - [viewController.view removeFromSuperview]; - if([self.visibleViewControllers containsObject:viewController]) { - [viewController endAppearanceTransition]; - } - - [viewController removeFromParentViewController]; - - [self.visibleControllers removeObject:viewController]; - } - - [self _updateBoundsAndConstraints]; - [self _tilePages]; - - if(completion) { - completion(); - } - }); + + if(animated && [self.layouter respondsToSelector:@selector(animatePageReloadAtIndex:oldViewController:newViewController:pageViewController:completion:)]) { + dispatch_group_enter(animationsDispatchGroup); + [self.layouter animatePageReloadAtIndex:pageIndex oldViewController:oldViewController newViewController:newViewController pageViewController:self completion:^{ + dispatch_group_leave(animationsDispatchGroup); + }]; + } + }]; + + dispatch_group_notify(animationsDispatchGroup, dispatch_get_main_queue(), ^{ + + for(UIViewController *viewController in removedViewControllers) { + [viewController.view removeFromSuperview]; + if([self.visibleViewControllers containsObject:viewController]) { + [viewController endAppearanceTransition]; + } + + [viewController removeFromParentViewController]; + + [self.visibleControllers removeObject:viewController]; + } + + [self _updateBoundsAndConstraints]; + [self _tilePages]; + + if(completion) { + completion(); + } + }); } - (void)insertPagesAtIndexes:(NSIndexSet *)indexes animated:(BOOL)animated completion:(void(^)(void))completion { - NSInteger oldNumberOfPages = self.numberOfPages; - self.numberOfPages = [self.dataSource numberOfPagesInPageViewController:self]; - - NSAssert((self.numberOfPages == oldNumberOfPages + indexes.count), @"Invalid number of pages after insertion. Expecting %lu and received %lu", (unsigned long)(oldNumberOfPages + indexes.count), (unsigned long)self.numberOfPages); - - self.insertionIndexes = indexes; - - dispatch_group_t animationsDispatchGroup = dispatch_group_create(); - - __block BOOL shouldAdjustOffset = NO; - [indexes enumerateIndexesUsingBlock:^(NSUInteger pageIndex, BOOL *stop) { + NSInteger oldNumberOfPages = self.numberOfPages; + self.numberOfPages = [self.dataSource numberOfPagesInPageViewController:self]; + + NSAssert((self.numberOfPages == oldNumberOfPages + indexes.count), @"Invalid number of pages after insertion. Expecting %lu and received %lu", (unsigned long)(oldNumberOfPages + indexes.count), (unsigned long)self.numberOfPages); + + self.insertionIndexes = indexes; + + dispatch_group_t animationsDispatchGroup = dispatch_group_create(); + + __block BOOL shouldAdjustOffset = NO; + [indexes enumerateIndexesUsingBlock:^(NSUInteger pageIndex, BOOL *stop) { BOOL shouldKeepCurrentPage = YES; if([self.layouter respondsToSelector:@selector(shouldPreserveOffsetForInsertionAtIndex:pageViewController:)]) { shouldKeepCurrentPage = [self.layouter shouldPreserveOffsetForInsertionAtIndex:pageIndex pageViewController:self]; } - if((shouldKeepCurrentPage && pageIndex <= self.currentPage) || (!shouldKeepCurrentPage && pageIndex < self.currentPage)) { - shouldAdjustOffset = YES; - *stop = YES; - } - }]; - - [indexes enumerateIndexesUsingBlock:^(NSUInteger pageIndex, BOOL *stop) { - - // Insert the new page - [self.pages insertObject:[NSNull null] atIndex:pageIndex]; - - // Animate page movements - if(animated && [self.layouter respondsToSelector:@selector(animatePageMoveFromIndex:toIndex:viewController:pageViewController:completion:)]) { - if(shouldAdjustOffset) { - for(NSInteger index = ((NSInteger)pageIndex - 1); index >= 0; index--) { - UIViewController *someController = [self viewControllerForPageAtIndex:index]; - dispatch_group_enter(animationsDispatchGroup); - [self.layouter animatePageMoveFromIndex:(index + 1) toIndex:index viewController:someController pageViewController:self completion:^{ - dispatch_group_leave(animationsDispatchGroup); - }]; - } - } else { - for(NSInteger index = (NSInteger)oldNumberOfPages; index >= (NSInteger)pageIndex; index--) { - UIViewController *someController = [self viewControllerForPageAtIndex:index]; - dispatch_group_enter(animationsDispatchGroup); - [self.layouter animatePageMoveFromIndex:(index - 1) toIndex:index viewController:someController pageViewController:self completion:^{ - dispatch_group_leave(animationsDispatchGroup); - }]; - } - } - } - }]; - - [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger pageIndex, BOOL *stop) { - - UIViewController *viewController = [self _createAndInsertNewPageAtIndex:pageIndex]; - - // Animate the page insertion - if(animated && [self.layouter respondsToSelector:@selector(animatePageInsertionAtIndex:viewController:pageViewController:completion:)]) { - - if(shouldAdjustOffset) { - [UIView performWithoutAnimation:^{ - CGRect frame = viewController.view.frame; - if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { - frame.origin.x -= CGRectGetWidth(viewController.view.bounds) * indexes.count; - } else { - frame.origin.y -= CGRectGetHeight(viewController.view.bounds) * indexes.count; - } - viewController.view.frame = frame; - }]; - } - - dispatch_group_enter(animationsDispatchGroup); - [self.layouter animatePageInsertionAtIndex:pageIndex viewController:viewController pageViewController:self completion:^{ - dispatch_group_leave(animationsDispatchGroup); - }]; - } - }]; - - void(^updateLayout)(void) = ^{ - if(shouldAdjustOffset) { - [self _blockContentOffsetOnPageAtIndex:(self.currentPage + indexes.count)]; - } - [self _updateBoundsAndConstraints]; - [self _tilePages]; - [self _unblockContentOffset]; - }; - - if(animated) { - dispatch_group_enter(animationsDispatchGroup); - [UIView animateWithDuration:self.animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ - updateLayout(); - } completion:^(BOOL finished) { - dispatch_group_leave(animationsDispatchGroup); - }]; - } else { - updateLayout(); - } - - dispatch_group_notify(animationsDispatchGroup, dispatch_get_main_queue(), ^{ - - self.insertionIndexes = nil; - [self _tilePages]; - - if(completion) { - completion(); - } - }); + if((shouldKeepCurrentPage && pageIndex <= self.currentPage) || (!shouldKeepCurrentPage && pageIndex < self.currentPage)) { + shouldAdjustOffset = YES; + *stop = YES; + } + }]; + + [indexes enumerateIndexesUsingBlock:^(NSUInteger pageIndex, BOOL *stop) { + + // Insert the new page + [self.pages insertObject:[NSNull null] atIndex:pageIndex]; + + // Animate page movements + if(animated && [self.layouter respondsToSelector:@selector(animatePageMoveFromIndex:toIndex:viewController:pageViewController:completion:)]) { + if(shouldAdjustOffset) { + for(NSInteger index = ((NSInteger)pageIndex - 1); index >= 0; index--) { + UIViewController *someController = [self viewControllerForPageAtIndex:index]; + dispatch_group_enter(animationsDispatchGroup); + [self.layouter animatePageMoveFromIndex:(index + 1) toIndex:index viewController:someController pageViewController:self completion:^{ + dispatch_group_leave(animationsDispatchGroup); + }]; + } + } else { + for(NSInteger index = (NSInteger)oldNumberOfPages; index >= (NSInteger)pageIndex; index--) { + UIViewController *someController = [self viewControllerForPageAtIndex:index]; + dispatch_group_enter(animationsDispatchGroup); + [self.layouter animatePageMoveFromIndex:(index - 1) toIndex:index viewController:someController pageViewController:self completion:^{ + dispatch_group_leave(animationsDispatchGroup); + }]; + } + } + } + }]; + + [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger pageIndex, BOOL *stop) { + + UIViewController *viewController = [self _createAndInsertNewPageAtIndex:pageIndex]; + + // Animate the page insertion + if(animated && [self.layouter respondsToSelector:@selector(animatePageInsertionAtIndex:viewController:pageViewController:completion:)]) { + + if(shouldAdjustOffset) { + [UIView performWithoutAnimation:^{ + CGRect frame = viewController.view.frame; + if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { + frame.origin.x -= CGRectGetWidth(viewController.view.bounds) * indexes.count; + } else { + frame.origin.y -= CGRectGetHeight(viewController.view.bounds) * indexes.count; + } + viewController.view.frame = frame; + }]; + } + + dispatch_group_enter(animationsDispatchGroup); + [self.layouter animatePageInsertionAtIndex:pageIndex viewController:viewController pageViewController:self completion:^{ + dispatch_group_leave(animationsDispatchGroup); + }]; + } + }]; + + void(^updateLayout)(void) = ^{ + if(shouldAdjustOffset) { + [self _blockContentOffsetOnPageAtIndex:(self.currentPage + indexes.count)]; + } + [self _updateBoundsAndConstraints]; + [self _tilePages]; + [self _unblockContentOffset]; + }; + + if(animated) { + dispatch_group_enter(animationsDispatchGroup); + [UIView animateWithDuration:self.animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ + updateLayout(); + } completion:^(BOOL finished) { + dispatch_group_leave(animationsDispatchGroup); + }]; + } else { + updateLayout(); + } + + dispatch_group_notify(animationsDispatchGroup, dispatch_get_main_queue(), ^{ + + self.insertionIndexes = nil; + [self _tilePages]; + + if(completion) { + completion(); + } + }); } - (void)deletePagesAtIndexes:(NSIndexSet *)indexes animated:(BOOL)animated completion:(void(^)(void))completion { - NSInteger oldNumberOfPages = self.numberOfPages; - self.numberOfPages = [self.dataSource numberOfPagesInPageViewController:self]; - NSAssert((self.numberOfPages == oldNumberOfPages - indexes.count), @"Invalid number of pages after removal. Expecting %lu and received %lu", (unsigned long)(oldNumberOfPages - indexes.count), (unsigned long)self.numberOfPages); - - __block BOOL shouldAdjustOffset = NO; - [indexes enumerateIndexesUsingBlock:^(NSUInteger pageIndex, BOOL *stop) { - if(pageIndex < self.currentPage) { - shouldAdjustOffset = YES; - *stop = YES; - } - }]; - - dispatch_group_t animationsDispatchGroup = dispatch_group_create(); - - NSMutableArray *removedViewControllers = [NSMutableArray array]; - - [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger pageIndex, BOOL *stop) { - - UIViewController *viewController = [self viewControllerForPageAtIndex:pageIndex]; - - if(viewController) { - [removedViewControllers addObject:viewController]; - } - - [viewController willMoveToParentViewController:nil]; - if([self.visibleViewControllers containsObject:viewController]) { - [viewController beginAppearanceTransition:NO animated:animated]; - } - - // Animate the deletion - if(animated && [self.layouter respondsToSelector:@selector(animatePageDeletionAtIndex:viewController:pageViewController:completion:)]) { - dispatch_group_enter(animationsDispatchGroup); - - NSInteger animationIndex = (shouldAdjustOffset ? pageIndex - 1 : pageIndex); - [self.layouter animatePageDeletionAtIndex:animationIndex viewController:viewController pageViewController:self completion:^{ - dispatch_group_leave(animationsDispatchGroup); - }]; - - if(shouldAdjustOffset) { - dispatch_group_enter(animationsDispatchGroup); - [UIView animateWithDuration:self.animationDuration animations:^{ - - CGRect frame = viewController.view.frame; - if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { - frame.origin.x -= CGRectGetWidth(viewController.view.bounds) * indexes.count; - } else { - frame.origin.y -= CGRectGetHeight(viewController.view.bounds) * indexes.count; - } - viewController.view.frame = frame; - } completion:^(BOOL finished) { - dispatch_group_leave(animationsDispatchGroup); - }]; - } + NSInteger oldNumberOfPages = self.numberOfPages; + self.numberOfPages = [self.dataSource numberOfPagesInPageViewController:self]; + NSAssert((self.numberOfPages == oldNumberOfPages - indexes.count), @"Invalid number of pages after removal. Expecting %lu and received %lu", (unsigned long)(oldNumberOfPages - indexes.count), (unsigned long)self.numberOfPages); + + __block BOOL shouldAdjustOffset = NO; + [indexes enumerateIndexesUsingBlock:^(NSUInteger pageIndex, BOOL *stop) { + if(pageIndex < self.currentPage) { + shouldAdjustOffset = YES; + *stop = YES; + } + }]; + + dispatch_group_t animationsDispatchGroup = dispatch_group_create(); + + NSMutableArray *removedViewControllers = [NSMutableArray array]; + + [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger pageIndex, BOOL *stop) { + + UIViewController *viewController = [self viewControllerForPageAtIndex:pageIndex]; + + if(viewController) { + [removedViewControllers addObject:viewController]; + } + + [viewController willMoveToParentViewController:nil]; + if([self.visibleViewControllers containsObject:viewController]) { + [viewController beginAppearanceTransition:NO animated:animated]; + } + + // Animate the deletion + if(animated && [self.layouter respondsToSelector:@selector(animatePageDeletionAtIndex:viewController:pageViewController:completion:)]) { + dispatch_group_enter(animationsDispatchGroup); + + NSInteger animationIndex = (shouldAdjustOffset ? pageIndex - 1 : pageIndex); + [self.layouter animatePageDeletionAtIndex:animationIndex viewController:viewController pageViewController:self completion:^{ + dispatch_group_leave(animationsDispatchGroup); + }]; + + if(shouldAdjustOffset) { + dispatch_group_enter(animationsDispatchGroup); + [UIView animateWithDuration:self.animationDuration animations:^{ + + CGRect frame = viewController.view.frame; + if(self.layouter.navigationType == SCPageLayouterNavigationTypeHorizontal) { + frame.origin.x -= CGRectGetWidth(viewController.view.bounds) * indexes.count; + } else { + frame.origin.y -= CGRectGetHeight(viewController.view.bounds) * indexes.count; + } + viewController.view.frame = frame; + } completion:^(BOOL finished) { + dispatch_group_leave(animationsDispatchGroup); + }]; + } } else { [viewController.view removeFromSuperview]; } - - // Update page indexes - [self.pages removeObjectAtIndex:pageIndex]; - }]; - - // Update the content offset and pages layout - void (^updateLayout)(void) = ^{ - if(shouldAdjustOffset) { - [self _blockContentOffsetOnPageAtIndex:(self.currentPage - indexes.count)]; - } - - [self _updateBoundsAndConstraints]; - [self _tilePages]; - [self _unblockContentOffset]; - }; - - if(animated) { - dispatch_group_enter(animationsDispatchGroup); - [UIView animateWithDuration:self.animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ - updateLayout(); - } completion:^(BOOL finished) { - dispatch_group_leave(animationsDispatchGroup); - }]; - } else { - updateLayout(); - } - - // Cleanup and notify of completion - dispatch_group_notify(animationsDispatchGroup, dispatch_get_main_queue(), ^{ - - for(UIViewController *viewController in removedViewControllers) { - [viewController.view removeFromSuperview]; - if([self.visibleViewControllers containsObject:viewController]) { - [viewController endAppearanceTransition]; - } - - [viewController removeFromParentViewController]; - [self.visibleControllers removeObject:viewController]; - } - - [self _tilePages]; - - if(completion) { - completion(); - } - }); + + // Update page indexes + [self.pages removeObjectAtIndex:pageIndex]; + }]; + + // Update the content offset and pages layout + void (^updateLayout)(void) = ^{ + if(shouldAdjustOffset) { + [self _blockContentOffsetOnPageAtIndex:(self.currentPage - indexes.count)]; + } + + [self _updateBoundsAndConstraints]; + [self _tilePages]; + [self _unblockContentOffset]; + }; + + if(animated) { + dispatch_group_enter(animationsDispatchGroup); + [UIView animateWithDuration:self.animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ + updateLayout(); + } completion:^(BOOL finished) { + dispatch_group_leave(animationsDispatchGroup); + }]; + } else { + updateLayout(); + } + + // Cleanup and notify of completion + dispatch_group_notify(animationsDispatchGroup, dispatch_get_main_queue(), ^{ + + for(UIViewController *viewController in removedViewControllers) { + [viewController.view removeFromSuperview]; + if([self.visibleViewControllers containsObject:viewController]) { + [viewController endAppearanceTransition]; + } + + [viewController removeFromParentViewController]; + [self.visibleControllers removeObject:viewController]; + } + + [self _tilePages]; + + if(completion) { + completion(); + } + }); } - (void)movePageAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex animated:(BOOL)animated completion:(void(^)(void))completion { - NSAssert(fromIndex < self.numberOfPages, @"Index out of bounds"); - NSAssert(toIndex < self.numberOfPages, @"Index out of bounds"); - NSAssert(self.numberOfPages > 1, @"Not enough pages in the page view controller"); - - if(fromIndex == toIndex) { - if(completion) { - completion(); - } - return; - } - - BOOL shouldAdjustOffset = (fromIndex < self.currentPage && toIndex > self.currentPage) || (fromIndex > self.currentPage && toIndex < self.currentPage); - - UIViewController *viewController = [self viewControllerForPageAtIndex:fromIndex]; - - dispatch_group_t animationsDispatchGroup = dispatch_group_create(); - - SCPageViewControllerPageDetails *pageDetails = [self.pages objectAtIndex:fromIndex]; - [self.pages removeObjectAtIndex:fromIndex]; - [self.pages insertObject:pageDetails atIndex:toIndex]; - - if(fromIndex < toIndex) { - for(NSInteger pageIndex = (NSInteger)fromIndex; pageIndex < (NSInteger)toIndex; pageIndex++) { - UIViewController *someController = [self viewControllerForPageAtIndex:pageIndex]; - if(someController) { - if(shouldAdjustOffset || !animated || ![self.layouter respondsToSelector:@selector(animatePageMoveFromIndex:toIndex:viewController:pageViewController:completion:)]) { - continue; - } - - dispatch_group_enter(animationsDispatchGroup); - [self.layouter animatePageMoveFromIndex:(pageIndex + 1) toIndex:pageIndex viewController:someController pageViewController:self completion:^{ - dispatch_group_leave(animationsDispatchGroup); - }]; - } - } - } else { - for(NSInteger pageIndex = (NSInteger)fromIndex; pageIndex >= (NSInteger)toIndex; pageIndex--) { - UIViewController *someController = [self viewControllerForPageAtIndex:pageIndex]; - if(someController) { - if(shouldAdjustOffset || !animated || ![self.layouter respondsToSelector:@selector(animatePageMoveFromIndex:toIndex:viewController:pageViewController:completion:)]) { - continue; - } - - dispatch_group_enter(animationsDispatchGroup); - [self.layouter animatePageMoveFromIndex:(pageIndex + 1) toIndex:pageIndex viewController:someController pageViewController:self completion:^{ - dispatch_group_leave(animationsDispatchGroup); - }]; - } - } - } - - // Update the scrollView's offset - if(shouldAdjustOffset) { - [self _blockContentOffsetOnPageAtIndex:self.currentPage]; - } - - if(!viewController) { - // Force load the missing page - viewController = [self _createAndInsertNewPageAtIndex:toIndex]; - [viewController.view setFrame:[self.layouter finalFrameForPageAtIndex:fromIndex pageViewController:self]]; - } - - if(animated && [self.layouter respondsToSelector:@selector(animatePageMoveFromIndex:toIndex:viewController:pageViewController:completion:)]) { - dispatch_group_enter(animationsDispatchGroup); - [self.layouter animatePageMoveFromIndex:fromIndex toIndex:toIndex viewController:viewController pageViewController:self completion:^{ - dispatch_group_leave(animationsDispatchGroup); - }]; - } - - dispatch_group_notify(animationsDispatchGroup, dispatch_get_main_queue(), ^{ - - if(shouldAdjustOffset) { - if(fromIndex < toIndex) { - [self _blockContentOffsetOnPageAtIndex:(self.currentPage - 1)]; - } else { - [self _blockContentOffsetOnPageAtIndex:(self.currentPage + 1)]; - } - } - - [self _updateBoundsAndConstraints]; - [self _tilePages]; - - [self _unblockContentOffset]; - - if(completion) { - completion(); - } - }); + NSAssert(fromIndex < self.numberOfPages, @"Index out of bounds"); + NSAssert(toIndex < self.numberOfPages, @"Index out of bounds"); + NSAssert(self.numberOfPages > 1, @"Not enough pages in the page view controller"); + + if(fromIndex == toIndex) { + if(completion) { + completion(); + } + return; + } + + BOOL shouldAdjustOffset = (fromIndex < self.currentPage && toIndex > self.currentPage) || (fromIndex > self.currentPage && toIndex < self.currentPage); + + UIViewController *viewController = [self viewControllerForPageAtIndex:fromIndex]; + + dispatch_group_t animationsDispatchGroup = dispatch_group_create(); + + SCPageViewControllerPageDetails *pageDetails = [self.pages objectAtIndex:fromIndex]; + [self.pages removeObjectAtIndex:fromIndex]; + [self.pages insertObject:pageDetails atIndex:toIndex]; + + if(fromIndex < toIndex) { + for(NSInteger pageIndex = (NSInteger)fromIndex; pageIndex < (NSInteger)toIndex; pageIndex++) { + UIViewController *someController = [self viewControllerForPageAtIndex:pageIndex]; + if(someController) { + if(shouldAdjustOffset || !animated || ![self.layouter respondsToSelector:@selector(animatePageMoveFromIndex:toIndex:viewController:pageViewController:completion:)]) { + continue; + } + + dispatch_group_enter(animationsDispatchGroup); + [self.layouter animatePageMoveFromIndex:(pageIndex + 1) toIndex:pageIndex viewController:someController pageViewController:self completion:^{ + dispatch_group_leave(animationsDispatchGroup); + }]; + } + } + } else { + for(NSInteger pageIndex = (NSInteger)fromIndex; pageIndex >= (NSInteger)toIndex; pageIndex--) { + UIViewController *someController = [self viewControllerForPageAtIndex:pageIndex]; + if(someController) { + if(shouldAdjustOffset || !animated || ![self.layouter respondsToSelector:@selector(animatePageMoveFromIndex:toIndex:viewController:pageViewController:completion:)]) { + continue; + } + + dispatch_group_enter(animationsDispatchGroup); + [self.layouter animatePageMoveFromIndex:(pageIndex + 1) toIndex:pageIndex viewController:someController pageViewController:self completion:^{ + dispatch_group_leave(animationsDispatchGroup); + }]; + } + } + } + + // Update the scrollView's offset + if(shouldAdjustOffset) { + [self _blockContentOffsetOnPageAtIndex:self.currentPage]; + } + + if(!viewController) { + // Force load the missing page + viewController = [self _createAndInsertNewPageAtIndex:toIndex]; + [viewController.view setFrame:[self.layouter finalFrameForPageAtIndex:fromIndex pageViewController:self]]; + } + + if(animated && [self.layouter respondsToSelector:@selector(animatePageMoveFromIndex:toIndex:viewController:pageViewController:completion:)]) { + dispatch_group_enter(animationsDispatchGroup); + [self.layouter animatePageMoveFromIndex:fromIndex toIndex:toIndex viewController:viewController pageViewController:self completion:^{ + dispatch_group_leave(animationsDispatchGroup); + }]; + } + + dispatch_group_notify(animationsDispatchGroup, dispatch_get_main_queue(), ^{ + + if(shouldAdjustOffset) { + if(fromIndex < toIndex) { + [self _blockContentOffsetOnPageAtIndex:(self.currentPage - 1)]; + } else { + [self _blockContentOffsetOnPageAtIndex:(self.currentPage + 1)]; + } + } + + [self _updateBoundsAndConstraints]; + [self _tilePages]; + + [self _unblockContentOffset]; + + if(completion) { + completion(); + } + }); } @end diff --git a/SCPageViewController/SCPageViewControllerView.m b/SCPageViewController/SCPageViewControllerView.m index 8f0733b..408593a 100644 --- a/SCPageViewController/SCPageViewControllerView.m +++ b/SCPageViewController/SCPageViewControllerView.m @@ -12,11 +12,11 @@ @implementation SCPageViewControllerView - (void)setFrame:(CGRect)frame { - [self.delegate pageViewControllerViewWillChangeFrame:self]; - - super.frame = frame; - - [self.delegate pageViewControllerViewDidChangeFrame:self]; + [self.delegate pageViewControllerViewWillChangeFrame:self]; + + super.frame = frame; + + [self.delegate pageViewControllerViewDidChangeFrame:self]; } @end