| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- #import "TYPagerController.h"
- #define _window_width [UIScreen mainScreen].bounds.size.width
- typedef NS_ENUM(NSUInteger, TYPagerControllerDirection) {
- TYPagerControllerLeft,
- TYPagerControllerRight,
- TYPagerControllerNone,
- };
- @interface TYPagerController ()<UIScrollViewDelegate> {
- BOOL _needLayoutContentView;
- BOOL _scrollAnimated;
- BOOL _isTapScrollMoved;
- CGFloat _preOffsetX;
-
- struct {
- unsigned int transitionFromIndexToIndex :1;
- unsigned int transitionFromIndexToIndexProgress :1;
- }_delegateFlags;
-
- struct {
- unsigned int transitionFromIndexToIndex :1;
- unsigned int transitionFromIndexToIndexProgress :1;
- }_methodFlags;
- }
- @property (nonatomic, weak) UIScrollView *contentView;
- @property (nonatomic, strong) NSMutableDictionary *visibleControllers;
- @property (nonatomic, strong) NSCache *memoryCache;
- @property (nonatomic, assign) NSInteger countOfControllers;
- //@property (nonatomic, assign) NSInteger curIndex;
- @property (nonatomic, assign) NSInteger curProgressIndex;
- @property (nonatomic, assign) NSRange visibleRange;
- @end
- NS_INLINE CGRect frameForControllerAtIndex(NSInteger index, CGRect frame)
- {
- return CGRectMake(index * CGRectGetWidth(frame), 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
- }
- NS_INLINE NSRange visibleRangWithOffset(CGFloat offset,CGFloat width, NSInteger maxIndex)
- {
- NSInteger startIndex = offset/width;
- NSInteger endIndex = ceil((offset + width)/width);
-
- if (startIndex < 0) {
- startIndex = 0;
- }
- if (endIndex > maxIndex) {
- endIndex = maxIndex;
- }
- return NSMakeRange(startIndex, endIndex - startIndex);
- }
- @implementation TYPagerController
- - (instancetype)initWithCoder:(NSCoder *)aDecoder
- {
- if (self = [super initWithCoder:aDecoder]) {
- [self configureInitPropertys];
- }
- return self;
- }
- - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
- {
- if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
- [self configureInitPropertys];
- }
- return self;
- }
- - (void)configureInitPropertys
- {
- _memoryCache = [[NSCache alloc]init];
- _changeIndexWhenScrollProgress = 0.5;
- _contentTopEdging = 8;
- }
- #pragma mark - life cycle
- - (void)viewDidLoad {
- [super viewDidLoad];
- // Do any additional setup after loading the view.
- if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) {
- self.edgesForExtendedLayout = UIRectEdgeNone;
- }
- self.view.backgroundColor = [UIColor whiteColor];
-
- // add horizenl scrollView
- [self addContentView];
- // set up propertys
- [self configurePropertys];
- }
- - (void)viewWillAppear:(BOOL)animated
- {
- [super viewWillAppear:animated];
-
- // [self layoutContentViewIfNeed];
- }
- - (void)viewWillLayoutSubviews
- {
- [super viewWillLayoutSubviews];
-
- // [self layoutContentViewIfNeed];
- }
- - (void)addContentView
- {
- UIScrollView *contentView = [[UIScrollView alloc]init];
- contentView.showsHorizontalScrollIndicator = NO;
- contentView.showsVerticalScrollIndicator = NO;
- contentView.pagingEnabled = YES;
- contentView.delegate = self;
- contentView.backgroundColor = kWhiteColor;
- contentView.bounces = NO;
-
- [self.view addSubview:contentView];
- _contentView = contentView;
- }
- #pragma mark - configre propertys
- - (void)configurePropertys
- {
- _visibleControllers = [NSMutableDictionary dictionary];
- _curIndex = 4;
- _curProgressIndex = 2;
- _preOffsetX = 0;
- _scrollAnimated = YES;
- [self configureMethods];
- }
- - (void)resetPropertys
- {
- [_memoryCache removeAllObjects];
- [_visibleControllers removeAllObjects];
-
- for (UIViewController *viewController in self.childViewControllers) {
- [self removeViewController:viewController];
- }
-
- // _curIndex = 1;
- // _curProgressIndex = 1;
- _preOffsetX = 0;
- }
- - (void)setDelegate:(id<TYPagerControllerDelegate>)delegate
- {
- _delegate = delegate;
-
- _delegateFlags.transitionFromIndexToIndex = [_delegate respondsToSelector:@selector(pagerController:transitionFromIndex:toIndex:animated:)];
- _delegateFlags.transitionFromIndexToIndexProgress = [_delegate respondsToSelector:@selector(pagerController:transitionFromIndex:toIndex:progress:)];
- }
- - (void)configureMethods
- {
- _methodFlags.transitionFromIndexToIndex = [self respondsToSelector:@selector(transitionFromIndex:toIndex:animated:)];
- _methodFlags.transitionFromIndexToIndexProgress = [self respondsToSelector:@selector(transitionFromIndex:toIndex:progress:)];
- }
- #pragma mark - public method
- - (void)reloadData
- {
- [self resetPropertys];
-
- [self updateContentView];
- }
- -(void)setContentFrame{
- [_contentView setContentOffset:CGPointMake(_window_width,0) animated:NO];
- }
- - (void)moveToControllerAtIndex:(NSInteger)index animated:(BOOL)animated
- {
- if (index < 0 || index >= _countOfControllers) {
- return;
- }
- _isTapScrollMoved = YES;
- _scrollAnimated = animated;
-
- [_contentView setContentOffset:CGPointMake(index * CGRectGetWidth(_contentView.frame),0) animated:NO];
-
- }
- - (NSArray *)visibleViewControllers
- {
- return [_visibleControllers allValues];
- }
- #pragma mark - layout content
- - (NSInteger)statusBarHeight
- {
- // return (_adjustStatusBarHeight && (!self.navigationController || self.navigationController.isNavigationBarHidden) && [[[UIDevice currentDevice] systemVersion] floatValue] >= 7) ? 20:0;
- return 20;
- }
- // if need layout contentView
- - (void)layoutContentViewIfNeed
- {
- // if (!CGSizeEqualToSize(_contentView.frame.size, CGSizeMake(CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - _contentTopEdging - [self statusBarHeight]))) {
- // // size changed
- // [self updateContentView];
- // }
- }
- // update content subViews
- - (void)updateContentView
- {
- _needLayoutContentView = YES;
- _countOfControllers = [_dataSource numberOfControllersInPagerController];
-
- CGFloat contentTopEdging = _contentTopEdging;
-
- [self reSizeContentViewWithFrame:CGRectMake(0, contentTopEdging, CGRectGetWidth(self.view.frame),kRealValue(1000))];
- // kScreenH - contentTopEdging)];
-
- [self layoutContentView];
- }
- // change content View size
- -(void)reSizeContentViewWithFrame:(CGRect)frame{
- _contentView.frame = frame;
- _contentView.contentSize = CGSizeMake(_countOfControllers * CGRectGetWidth(_contentView.frame), frame.size.height);
- _contentView.contentOffset = CGPointMake(CGRectGetWidth(_contentView.frame), 0);
- }
- // layout content subViews
- - (void)layoutContentView
- {
- CGFloat offsetX = _contentView.contentOffset.x;
- // 获取可见range
- NSRange visibleRange = visibleRangWithOffset(offsetX, CGRectGetWidth(_contentView.frame), _countOfControllers);
- if (NSEqualRanges(_visibleRange, visibleRange) && !_needLayoutContentView) {
- return;
- }
- _needLayoutContentView = NO;
- _visibleRange = visibleRange;
-
- [self removeControllersOutOfVisibleRange:_visibleRange];
-
- [self addControllersInVisibleRange:_visibleRange];
- }
- // caculate pager index
- - (void)configurePagerIndex
- {
- CGFloat offsetX = _contentView.contentOffset.x;
- CGFloat width = CGRectGetWidth(_contentView.frame);
- // scroll direction
- TYPagerControllerDirection direction = offsetX >= _preOffsetX ? TYPagerControllerLeft : TYPagerControllerRight;
-
- NSInteger index = 0;
- // when scroll progress percent will change index
- CGFloat percentChangeIndex = 1.0-_changeIndexWhenScrollProgress;
-
- // caculate cur index
- if (direction == TYPagerControllerLeft) {
- index = offsetX/width+percentChangeIndex;
- }else {
- index = ceil(offsetX/width-percentChangeIndex);
- }
-
- if (index < 0) {
- index = 0;
- }else if (index >= _countOfControllers) {
- index = _countOfControllers-1;
- }
- // if index not same,change index
- if (index != _curIndex) {
- NSInteger fromIndex = _curIndex;
- _curIndex = index;
-
- if (_methodFlags.transitionFromIndexToIndex) {
- [self transitionFromIndex:fromIndex toIndex:_curIndex animated:_scrollAnimated];
- }
- if (_delegateFlags.transitionFromIndexToIndex) {
- [_delegate pagerController:self transitionFromIndex:fromIndex toIndex:_curIndex animated:_scrollAnimated];
- }
- }
- _scrollAnimated = YES;
- }
- // caculate pager index and progress
- - (void)configurePagerIndexByProgress
- {
- CGFloat offsetX = _contentView.contentOffset.x;
- CGFloat width = CGRectGetWidth(_contentView.frame);
- CGFloat floorIndex = floor(offsetX/width);
- CGFloat progress = offsetX/width-floorIndex;
-
- if (floorIndex < 0 || floorIndex >= _countOfControllers) {
- return;
- }
-
- TYPagerControllerDirection direction = offsetX >= _preOffsetX ? TYPagerControllerLeft : TYPagerControllerRight;
-
- NSInteger fromIndex = 0, toIndex = 0;
-
- if (direction == TYPagerControllerLeft) {
- if (floorIndex >= _countOfControllers -1) {
- return;
- }
- fromIndex = floorIndex;
- toIndex = MIN(_countOfControllers-1, fromIndex + 1);
- }else {
- if (floorIndex < 0 ) {
- return;
- }
- toIndex = floorIndex;
- fromIndex = MIN(_countOfControllers-1, toIndex +1);
- progress = 1.0 - progress;
- }
-
- if (_methodFlags.transitionFromIndexToIndexProgress) {
- [self transitionFromIndex:fromIndex toIndex:toIndex progress:progress];
- }
-
- if (_delegateFlags.transitionFromIndexToIndexProgress) {
- [_delegate pagerController:self transitionFromIndex:fromIndex toIndex:toIndex progress:progress];
- }
- }
- // is scrolling and caculate progess ?
- - (BOOL)isProgressScrollEnabel
- {
- return (_delegateFlags.transitionFromIndexToIndexProgress || _methodFlags.transitionFromIndexToIndexProgress) && !_isTapScrollMoved ;
- }
- #pragma mark - remove controller
- // remove pager controller if it out of visible range
- - (void)removeControllersOutOfVisibleRange:(NSRange)range
- {
- NSMutableArray *deleteArray = [NSMutableArray array];
- [_visibleControllers enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, UIViewController *viewController, BOOL * stop) {
- NSInteger indexOfController = [key integerValue];
-
- if (!NSLocationInRange(indexOfController, range)) {
- // unvisible
- [self removeViewController:viewController atIndex:indexOfController];
- [deleteArray addObject:key];
- }else {
- [self addViewController:viewController atIndex:indexOfController];
- }
- }];
- [_visibleControllers removeObjectsForKeys:deleteArray];
- }
- // remove pager controller at index
- - (void)removeViewController:(UIViewController *)viewController atIndex:(NSInteger)index
- {
- if (viewController.parentViewController) {
- [self removeViewController:viewController];
- // remove and cache
- if (![_memoryCache objectForKey:@(index)]) {
- [_memoryCache setObject:viewController forKey:@(index)];
- }
- }
- }
- - (void)removeViewController:(UIViewController *)viewController
- {
- [viewController willMoveToParentViewController:nil];
- [viewController.view removeFromSuperview];
- [viewController removeFromParentViewController];
- }
- #pragma mark - add controller
- // add pager controller if it in visible range
- - (void)addControllersInVisibleRange:(NSRange)range
- {
- NSInteger endIndex = range.location + range.length;
- for (NSInteger idx = range.location ; idx < endIndex; ++idx) {
- UIViewController *viewController = [_visibleControllers objectForKey:@(idx)];
- if (!viewController) {
- viewController = [_memoryCache objectForKey:@(idx)];
- }
- if (!viewController) {
- viewController = [_dataSource pagerController:self controllerForIndex:idx];
- }
- [self addViewController:viewController atIndex:idx];
- }
- }
- // add pager controller at index
- - (void)addViewController:(UIViewController *)viewController atIndex:(NSInteger)index
- {
- if (!viewController.parentViewController) {
- // addChildViewController
- [self addChildViewController:viewController];
- [_contentView addSubview:viewController.view];
- [viewController didMoveToParentViewController:self];
-
- if (![_visibleControllers objectForKey:@(index)]) {
- [_visibleControllers setObject:viewController forKey:@(index)];
- }
- }else {
- // if VC have parentViewController,change the frame
- // viewController.view.frame = frameForControllerAtIndex(index, _contentVCFrame);
- }
- viewController.view.frame = CGRectMake(index * kScreenW, 0, kScreenW, kScreenH);
- }
- #pragma mark - UIScrollViewDelegate
- - (void)scrollViewDidScroll:(UIScrollView *)scrollView
- {
- if (scrollView == _contentView && _countOfControllers > 0) {
- if ([self isProgressScrollEnabel] && !_needLayoutContentView) {
- // caculate scroll progress
- [self configurePagerIndexByProgress];
- }
-
- if (!_needLayoutContentView) {
- // caculate scroll index
- [self configurePagerIndex];
- }
-
- // layout
- [self layoutContentView];
-
- _isTapScrollMoved = NO;
- }
- }
- - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
- {
- if (scrollView == _contentView) {
- // save offsetX ,judge scroll direction
- _preOffsetX = scrollView.contentOffset.x;
- }
- }
- - (void)didReceiveMemoryWarning {
- [super didReceiveMemoryWarning];
- // Dispose of any resources that can be recreated.
- [_memoryCache removeAllObjects];
- }
- - (void)dealloc
- {
- [_memoryCache removeAllObjects];
- [_visibleControllers removeAllObjects];
- }
- @end
|