XLPagerTabStrip/XLPagerTabStrip/XL/Controllers/XLPagerTabStripViewControll...

448 lines
17 KiB
Objective-C

//
// XLPagerTabStripViewController
// XLPagerTabStrip ( https://github.com/xmartlabs/XLPagerTabStrip )
//
// Copyright (c) 2015 Xmartlabs ( http://xmartlabs.com )
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "XLPagerTabStripViewController.h"
@interface XLPagerTabStripViewController ()
@property (nonatomic) NSUInteger currentIndex;
@end
@implementation XLPagerTabStripViewController
{
NSUInteger _lastPageNumber;
CGFloat _lastContentOffset;
NSUInteger _pageBeforeRotate;
NSArray * _originalPagerTabStripChildViewControllers;
CGSize _lastSize;
}
@synthesize currentIndex = _currentIndex;
#pragma maek - initializers
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self){
[self pagerTabStripViewControllerInit];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self){
[self pagerTabStripViewControllerInit];
}
return self;
}
-(void)pagerTabStripViewControllerInit
{
_currentIndex = 0;
_delegate = self;
_dataSource = self;
_lastContentOffset = 0.0f;
_isElasticIndicatorLimit = NO;
_skipIntermediateViewControllers = YES;
_isProgressiveIndicator = NO;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (!self.containerView){
self.containerView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds))];
self.containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.containerView];
}
self.containerView.bounces = YES;
[self.containerView setAlwaysBounceHorizontal:YES];
[self.containerView setAlwaysBounceVertical:NO];
self.containerView.scrollsToTop = NO;
self.containerView.delegate = self;
self.containerView.showsVerticalScrollIndicator = NO;
self.containerView.showsHorizontalScrollIndicator = NO;
self.containerView.pagingEnabled = YES;
if (self.dataSource){
_pagerTabStripChildViewControllers = [self.dataSource childViewControllersForPagerTabStripViewController:self];
}
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
_lastSize = self.containerView.bounds.size;
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self updateIfNeeded];
}
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self updateIfNeeded];
if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedAscending){
// SYSTEM_VERSION_LESS_THAN 8.0
[self.view layoutSubviews];
}
}
#pragma mark - move to another view controller
-(void)moveToViewControllerAtIndex:(NSUInteger)index
{
[self moveToViewControllerAtIndex:index animated:YES];
}
-(void)moveToViewControllerAtIndex:(NSUInteger)index animated:(bool)animated
{
if (![self isViewLoaded]){
self.currentIndex = index;
}
else{
if (self.skipIntermediateViewControllers && ABS(self.currentIndex - index) > 1){
NSArray * originalPagerTabStripChildViewControllers = self.pagerTabStripChildViewControllers;
NSMutableArray * tempChildViewControllers = [NSMutableArray arrayWithArray:originalPagerTabStripChildViewControllers];
UIViewController * currentChildVC = [originalPagerTabStripChildViewControllers objectAtIndex:self.currentIndex];
NSUInteger fromIndex = (self.currentIndex < index) ? index - 1 : index + 1;
[tempChildViewControllers setObject:[originalPagerTabStripChildViewControllers objectAtIndex:fromIndex] atIndexedSubscript:self.currentIndex];
[tempChildViewControllers setObject:currentChildVC atIndexedSubscript:fromIndex];
_pagerTabStripChildViewControllers = tempChildViewControllers;
[self.containerView setContentOffset:CGPointMake([self pageOffsetForChildIndex:fromIndex], 0) animated:NO];
if (self.navigationController){
self.navigationController.view.userInteractionEnabled = NO;
}
else{
self.view.userInteractionEnabled = NO;
}
_originalPagerTabStripChildViewControllers = originalPagerTabStripChildViewControllers;
[self.containerView setContentOffset:CGPointMake([self pageOffsetForChildIndex:index], 0) animated:YES];
}
else{
[self.containerView setContentOffset:CGPointMake([self pageOffsetForChildIndex:index], 0) animated:animated];
}
}
}
-(void)moveToViewController:(UIViewController *)viewController
{
[self moveToViewControllerAtIndex:[self.pagerTabStripChildViewControllers indexOfObject:viewController]];
}
#pragma mark - XLPagerTabStripViewControllerDelegate
-(void)pagerTabStripViewController:(XLPagerTabStripViewController *)pagerTabStripViewController
updateIndicatorFromIndex:(NSInteger)fromIndex
toIndex:(NSInteger)toIndex{
}
-(void)pagerTabStripViewController:(XLPagerTabStripViewController *)pagerTabStripViewController
updateIndicatorFromIndex:(NSInteger)fromIndex
toIndex:(NSInteger)toIndex
withProgressPercentage:(CGFloat)progressPercentage
{
}
#pragma mark - XLPagerTabStripViewControllerDataSource
-(NSArray *)childViewControllersForPagerTabStripViewController:(XLPagerTabStripViewController *)pagerTabStripViewController
{
return self.pagerTabStripChildViewControllers;
}
#pragma mark - Helpers
-(void)updateIfNeeded
{
if (!CGSizeEqualToSize(_lastSize, self.containerView.bounds.size)){
[self updateContent];
}
}
-(XLPagerTabStripDirection)scrollDirection
{
if (self.containerView.contentOffset.x > _lastContentOffset){
return XLPagerTabStripDirectionLeft;
}
else if (self.containerView.contentOffset.x < _lastContentOffset){
return XLPagerTabStripDirectionRight;
}
return XLPagerTabStripDirectionNone;
}
-(BOOL)canMoveToIndex:(NSUInteger)index
{
return (self.currentIndex != index && self.pagerTabStripChildViewControllers.count > index);
}
-(CGFloat)pageOffsetForChildIndex:(NSUInteger)index
{
return (index * CGRectGetWidth(self.containerView.bounds));
}
-(CGFloat)offsetForChildIndex:(NSUInteger)index
{
return (index * CGRectGetWidth(self.containerView.bounds) + ((CGRectGetWidth(self.containerView.bounds) - CGRectGetWidth(self.view.bounds)) * 0.5));
}
-(CGFloat)offsetForChildViewController:(UIViewController *)viewController
{
NSInteger index = [self.pagerTabStripChildViewControllers indexOfObject:viewController];
if (index == NSNotFound){
@throw [NSException exceptionWithName:NSRangeException reason:nil userInfo:nil];
}
return [self offsetForChildIndex:index];
}
-(NSUInteger)pageForContentOffset:(CGFloat)contentOffset
{
NSInteger result = [self virtualPageForContentOffset:contentOffset];
return [self pageForVirtualPage:result];
}
-(NSInteger)virtualPageForContentOffset:(CGFloat)contentOffset
{
NSInteger result = (contentOffset + (1.5f * [self pageWidth])) / [self pageWidth];
return result - 1;
}
-(NSUInteger)pageForVirtualPage:(NSInteger)virtualPage
{
if (virtualPage < 0){
return 0;
}
if (virtualPage > self.pagerTabStripChildViewControllers.count - 1){
return self.pagerTabStripChildViewControllers.count - 1;
}
return virtualPage;
}
-(CGFloat)pageWidth
{
return CGRectGetWidth(self.containerView.bounds);
}
-(CGFloat)scrollPercentage
{
if ([self scrollDirection] == XLPagerTabStripDirectionLeft || [self scrollDirection] == XLPagerTabStripDirectionNone){
return fmodf(self.containerView.contentOffset.x, [self pageWidth]) / [self pageWidth];
}
return 1 - fmodf(self.containerView.contentOffset.x >= 0 ? self.containerView.contentOffset.x : [self pageWidth] + self.containerView.contentOffset.x, [self pageWidth]) / [self pageWidth];
}
-(void)updateContent
{
if (!CGSizeEqualToSize(_lastSize, self.containerView.bounds.size)){
_lastSize = self.containerView.bounds.size;
[self.containerView setContentOffset:CGPointMake([self pageOffsetForChildIndex:self.currentIndex], 0) animated:NO];
}
NSArray * childViewControllers = self.pagerTabStripChildViewControllers;
self.containerView.contentSize = CGSizeMake(CGRectGetWidth(self.containerView.bounds) * childViewControllers.count, self.containerView.contentSize.height);
[childViewControllers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UIViewController * childController = (UIViewController *)obj;
CGFloat pageOffsetForChild = [self pageOffsetForChildIndex:idx];
if (fabs(self.containerView.contentOffset.x - pageOffsetForChild) < CGRectGetWidth(self.containerView.bounds)){
if (![childController parentViewController]){
[self addChildViewController:childController];
[childController didMoveToParentViewController:self];
CGFloat childPosition = [self offsetForChildIndex:idx];
[childController.view setFrame:CGRectMake(childPosition, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.containerView.bounds))];
childController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self.containerView addSubview:childController.view];
}
else{
CGFloat childPosition = [self offsetForChildIndex:idx];
[childController.view setFrame:CGRectMake(childPosition, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.containerView.bounds))];
childController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
}
}
else{
if ([childController parentViewController]){
[childController.view removeFromSuperview];
[childController willMoveToParentViewController:nil];
[childController removeFromParentViewController];
}
}
}];
NSUInteger oldCurrentIndex = self.currentIndex;
NSInteger virtualPage = [self virtualPageForContentOffset:self.containerView.contentOffset.x];
NSUInteger newCurrentIndex = [self pageForVirtualPage:virtualPage];
self.currentIndex = newCurrentIndex;
if (self.isProgressiveIndicator){
if ([self.delegate respondsToSelector:@selector(pagerTabStripViewController:updateIndicatorFromIndex:toIndex:withProgressPercentage:)]){
CGFloat scrollPercentage = [self scrollPercentage];
if (scrollPercentage > 0) {
NSInteger fromIndex = self.currentIndex;
NSInteger toIndex = self.currentIndex;
XLPagerTabStripDirection scrollDirection = [self scrollDirection];
if (scrollDirection == XLPagerTabStripDirectionLeft){
if (virtualPage > self.pagerTabStripChildViewControllers.count - 1){
fromIndex = self.pagerTabStripChildViewControllers.count - 1;
toIndex = self.pagerTabStripChildViewControllers.count;
}
else{
if (scrollPercentage > 0.5f){
fromIndex = MAX(toIndex - 1, 0);
}
else{
toIndex = fromIndex + 1;
}
}
}
else if (scrollDirection == XLPagerTabStripDirectionRight) {
if (virtualPage < 0){
fromIndex = 0;
toIndex = -1;
}
else{
if (scrollPercentage > 0.5f){
fromIndex = MIN(toIndex + 1, self.pagerTabStripChildViewControllers.count - 1);
}
else{
toIndex = fromIndex - 1;
}
}
}
[self.delegate pagerTabStripViewController:self updateIndicatorFromIndex:fromIndex toIndex:toIndex withProgressPercentage:(self.isElasticIndicatorLimit ? scrollPercentage : ( toIndex < 0 || toIndex >= self.pagerTabStripChildViewControllers.count ? 0 : scrollPercentage ))];
}
}
}
else{
if ([self.delegate respondsToSelector:@selector(pagerTabStripViewController:updateIndicatorFromIndex:toIndex:)] && oldCurrentIndex != newCurrentIndex){
[self.delegate pagerTabStripViewController:self
updateIndicatorFromIndex:MIN(oldCurrentIndex, self.pagerTabStripChildViewControllers.count - 1)
toIndex:newCurrentIndex];
}
}
}
-(void)reloadPagerTabStripView
{
if ([self isViewLoaded]){
[self.pagerTabStripChildViewControllers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UIViewController * childController = (UIViewController *)obj;
if ([childController parentViewController]){
[childController.view removeFromSuperview];
[childController willMoveToParentViewController:nil];
[childController removeFromParentViewController];
}
}];
_pagerTabStripChildViewControllers = self.dataSource ? [self.dataSource childViewControllersForPagerTabStripViewController:self] : @[];
self.containerView.contentSize = CGSizeMake(CGRectGetWidth(self.containerView.bounds) * _pagerTabStripChildViewControllers.count, self.containerView.contentSize.height);
if (self.currentIndex >= _pagerTabStripChildViewControllers.count){
self.currentIndex = _pagerTabStripChildViewControllers.count - 1;
}
[self.containerView setContentOffset:CGPointMake([self pageOffsetForChildIndex:self.currentIndex], 0) animated:NO];
[self updateContent];
}
}
#pragma mark - UIScrollViewDelegte
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (self.containerView == scrollView){
[self updateContent];
}
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (self.containerView == scrollView){
_lastPageNumber = [self pageForContentOffset:scrollView.contentOffset.x];
_lastContentOffset = scrollView.contentOffset.x;
}
}
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
if (self.containerView == scrollView && _originalPagerTabStripChildViewControllers){
_pagerTabStripChildViewControllers = _originalPagerTabStripChildViewControllers;
_originalPagerTabStripChildViewControllers = nil;
if (self.navigationController){
self.navigationController.view.userInteractionEnabled = YES;
}
else{
self.view.userInteractionEnabled = YES;
}
[self updateContent];
}
}
#pragma mark - Orientation
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
_pageBeforeRotate = self.currentIndex;
__typeof__(self) __weak weakSelf = self;
UIInterfaceOrientation fromOrientation = [[UIApplication sharedApplication] statusBarOrientation];
[coordinator animateAlongsideTransition:nil
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[weakSelf didRotateFromInterfaceOrientation:fromOrientation];
}];
}
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
_pageBeforeRotate = self.currentIndex;
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
self.currentIndex = _pageBeforeRotate;
[self updateIfNeeded];
}
@end