// // 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)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; _pageBeforeRotate = self.currentIndex; __typeof__(self) __weak weakSelf = self; UIInterfaceOrientation fromOrientation = [[UIApplication sharedApplication] statusBarOrientation]; [coordinator animateAlongsideTransition:nil completion:^(id context) { [weakSelf didRotateFromInterfaceOrientation:fromOrientation]; }]; } -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { _pageBeforeRotate = self.currentIndex; } -(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { self.currentIndex = _pageBeforeRotate; [self updateIfNeeded]; } @end