单军华
2018-07-11 7b02207537d35bfa1714bf8beafc921f717d100a
screendisplay/Pods/ZJScrollPageView/ZJScrollPageView/ZJScrollPageView/ZJContentView.m
New file
@@ -0,0 +1,455 @@
//
//  ZJContentView.m
//  ZJScrollPageView
//
//  Created by jasnig on 16/5/6.
//  Copyright © 2016年 ZeroJ. All rights reserved.
//
#import "ZJContentView.h"
#import "ZJScrollSegmentView.h"
#import "UIViewController+ZJScrollPageController.h"
typedef NS_ENUM(NSInteger, ZJScrollPageControllerScrollDirection) {
    ZJScrollPageControllerScrollDirectionNone,
    ZJScrollPageControllerScrollDirectionLeft,
    ZJScrollPageControllerScrollDirectionRight
};
@interface ZJContentView ()<UIScrollViewDelegate, UIGestureRecognizerDelegate> {
    CGFloat   _oldOffSetX;
    BOOL _isLoadFirstView;
//    BOOL _isAnimating;
}
/** 避免循环引用*/
@property (weak, nonatomic) ZJScrollSegmentView *segmentView;
// 父类 用于处理添加子控制器  使用weak避免循环引用
@property (weak, nonatomic) UIViewController *parentViewController;
// 当这个属性设置为YES的时候 就不用处理 scrollView滚动的计算
@property (assign, nonatomic) BOOL forbidTouchToAdjustPosition;
@property (assign, nonatomic) NSInteger itemsCount;
// 所有的子控制器
@property (strong, nonatomic) NSMutableDictionary<NSString *, UIViewController<ZJScrollPageViewChildVcDelegate> *> *childVcsDic;
// 当前控制器
@property (strong, nonatomic) UIViewController<ZJScrollPageViewChildVcDelegate> *currentChildVc;
/// 如果类似cell缓存一样, 虽然创建的控制器少了, 但是每个页面每次都要重新加载数据, 否则显示的内容就会出错, 貌似还不如每个页面创建一个控制器好
//@property (strong, nonatomic) NSCache *cacheChildVcs;
@property (strong, nonatomic) UIScrollView *scrollView;
@property (strong, nonatomic) UIView *currentView;
@property (strong, nonatomic) UIView *oldView;
@property (assign, nonatomic) NSInteger currentIndex;
@property (assign, nonatomic) NSInteger oldIndex;
@property (assign, nonatomic) ZJScrollPageControllerScrollDirection scrollDirection;
@end
@implementation ZJContentView
#define cellID @"cellID"
static NSString *const kContentOffsetOffKey = @"contentOffset";
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame segmentView:(ZJScrollSegmentView *)segmentView parentViewController:(UIViewController *)parentViewController delegate:(id<ZJScrollPageViewDelegate>) delegate {
    if (self = [super initWithFrame:frame]) {
        self.segmentView = segmentView;
        self.delegate = delegate;
        self.parentViewController = parentViewController;
        [self commonInit];
        [self addNotification];
    }
    return self;
}
- (void)commonInit {
    _oldIndex = -1;
    _currentIndex = -1;
    _oldOffSetX = 0.0f;
    _forbidTouchToAdjustPosition = NO;
    _isLoadFirstView = YES;
    if ([_delegate respondsToSelector:@selector(numberOfChildViewControllers)]) {
        self.itemsCount = [_delegate numberOfChildViewControllers];
    }
    [self addSubview:self.scrollView];
    [self setCurrentIndex:0 andScrollDirection:ZJScrollPageControllerScrollDirectionNone];
    if (self.parentViewController.parentViewController && [self.parentViewController.parentViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController *navi = (UINavigationController *)self.parentViewController.parentViewController;
        if (navi.interactivePopGestureRecognizer) {
            navi.interactivePopGestureRecognizer.delegate = self;
            [self.scrollView.panGestureRecognizer requireGestureRecognizerToFail:navi.interactivePopGestureRecognizer];
        }
    }
}
- (void)addNotification {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMemoryWarningHander:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
- (void)receiveMemoryWarningHander:(NSNotificationCenter *)noti {
    __weak typeof(self) weakSelf = self;
    [_childVcsDic enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, UIViewController<ZJScrollPageViewChildVcDelegate> * _Nonnull childVc, BOOL * _Nonnull stop) {
        __strong typeof(self) strongSelf = weakSelf;
        if (childVc != strongSelf.currentChildVc) {
            [_childVcsDic removeObjectForKey:key];
            [ZJContentView removeChildVc:childVc];
        }
    }];
}
- (void)dealloc {
    self.parentViewController = nil;
    [[NSNotificationCenter defaultCenter] removeObserver:self];
#if DEBUG
    NSLog(@"ZJContentView---销毁");
#endif
}
#pragma mark - public helper
/** 给外界可以设置ContentOffSet的方法 */
- (void)setContentOffSet:(CGPoint)offset animated:(BOOL)animated {
    self.forbidTouchToAdjustPosition = YES;
    NSInteger currentIndex = offset.x/self.scrollView.bounds.size.width;
    _oldIndex = _currentIndex;
    [self setCurrentIndex:currentIndex andScrollDirection:ZJScrollPageControllerScrollDirectionNone];
    if (animated) {
        CGFloat delta = offset.x - self.scrollView.contentOffset.x;
        NSInteger page = fabs(delta)/self.scrollView.bounds.size.width;
        if (page>=2) {// 需要滚动两页以上的时候, 跳过中间页的动画
            __weak typeof(self) weakself = self;
            dispatch_async(dispatch_get_main_queue(), ^{
                __strong typeof(weakself) strongSelf = weakself;
                if (strongSelf) {
                    [strongSelf.scrollView setContentOffset:offset animated:NO];
                    [self didAppearWithIndex:currentIndex];
                    [self didDisappearWithIndex:_oldIndex];
                }
            });
        }
        else {
            [self.scrollView setContentOffset:offset animated:animated];
            [self didAppearWithIndex:currentIndex];
            [self didDisappearWithIndex:_oldIndex];
        }
    }
    else {
        [self.scrollView setContentOffset:offset animated:animated];
        [self didAppearWithIndex:currentIndex];
        [self didDisappearWithIndex:_oldIndex];
    }
}
/** 给外界刷新视图的方法 */
- (void)reload {
    [self.childVcsDic enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, UIViewController<ZJScrollPageViewChildVcDelegate> * _Nonnull childVc, BOOL * _Nonnull stop) {
        [ZJContentView removeChildVc:childVc];
        childVc = nil;
    }];
    self.childVcsDic = nil;
    [self.scrollView removeFromSuperview];
    self.scrollView = nil;
    [self commonInit];
}
+ (void)removeChildVc:(UIViewController *)childVc {
    [childVc willMoveToParentViewController:nil];
    [childVc.view removeFromSuperview];
    [childVc removeFromParentViewController];
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (self.forbidTouchToAdjustPosition) {// first or last
        return;
    }
    if (scrollView.contentOffset.x <= 0) {
        [self contentViewDidMoveFromIndex:0 toIndex:0 progress:1.0];
        return;
    }
    if (scrollView.contentOffset.x >= scrollView.contentSize.width - scrollView.bounds.size.width) {
        [self contentViewDidMoveFromIndex:_itemsCount-1 toIndex:_itemsCount-1 progress:1.0];
        return;
    }
    CGFloat tempProgress = scrollView.contentOffset.x / self.bounds.size.width;
    NSInteger tempIndex = (NSInteger)tempProgress;
    CGFloat progress = tempProgress - floor(tempProgress);
    CGFloat deltaX = scrollView.contentOffset.x - _oldOffSetX;
    if (deltaX > 0 && (deltaX != scrollView.bounds.size.width)) {// 向右
        _oldIndex = tempIndex;
        NSInteger tempCurrentIndex = tempIndex + 1;
        [self setCurrentIndex:tempCurrentIndex andScrollDirection:ZJScrollPageControllerScrollDirectionRight];
    }
    else if (deltaX < 0) {
        progress = 1.0 - progress;
         _oldIndex = tempIndex + 1;
         [self setCurrentIndex:tempIndex andScrollDirection:ZJScrollPageControllerScrollDirectionLeft];
    }
    else {
         return;
    }
    NSLog(@"%f------%ld----%ld------", progress, _oldIndex, _currentIndex);
    [self contentViewDidMoveFromIndex:_oldIndex toIndex:_currentIndex progress:progress];
}
/** 滚动减速完成时再更新title的位置 */
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    NSInteger currentIndex = (scrollView.contentOffset.x / self.bounds.size.width);
    if (_scrollDirection == ZJScrollPageControllerScrollDirectionNone && !_forbidTouchToAdjustPosition) { // 开启bounds 在第一页和最后一页快速松开手又接触滑动的时候 会不合理的被调用这个代理方法 ---- 其实这个时候并没有在松开手的情况下减速完成
        return;
    }
    [self contentViewDidMoveFromIndex:currentIndex toIndex:currentIndex progress:1.0];
    // 调整title
    [self adjustSegmentTitleOffsetToCurrentIndex:currentIndex];
    if (scrollView.contentOffset.x == _oldOffSetX) {// 滚动未完成
        [self didAppearWithIndex:currentIndex];
        [self didDisappearWithIndex:_currentIndex];
    }
    else {
        [self didAppearWithIndex:_currentIndex];
        [self didDisappearWithIndex:_oldIndex];
    }
    // 重置_currentIndex 不触发set方法
    _currentIndex = currentIndex;
    _scrollDirection = ZJScrollPageControllerScrollDirectionNone;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    _oldOffSetX = scrollView.contentOffset.x;
    self.forbidTouchToAdjustPosition = NO;
    _scrollDirection = ZJScrollPageControllerScrollDirectionNone;
}
#pragma mark - private helper
- (void)contentViewDidMoveFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex progress:(CGFloat)progress {
    if(self.segmentView) {
        [self.segmentView adjustUIWithProgress:progress oldIndex:fromIndex currentIndex:toIndex];
    }
}
- (void)adjustSegmentTitleOffsetToCurrentIndex:(NSInteger)index {
    if(self.segmentView) {
        [self.segmentView adjustTitleOffSetToCurrentIndex:index];
    }
}
- (void)willAppearWithIndex:(NSInteger)index {
    UIViewController<ZJScrollPageViewChildVcDelegate> *controller = [self.childVcsDic valueForKey:[NSString stringWithFormat:@"%ld", index]];
    if (controller) {
        if ([controller respondsToSelector:@selector(zj_viewWillAppearForIndex:)]) {
            [controller zj_viewWillAppearForIndex:index];
        }
        if (_delegate && [_delegate respondsToSelector:@selector(scrollPageController:childViewControllWillAppear:forIndex:)]) {
            [_delegate scrollPageController:self.parentViewController childViewControllWillAppear:controller forIndex:index];
        }
    }
}
- (void)didAppearWithIndex:(NSInteger)index {
    UIViewController<ZJScrollPageViewChildVcDelegate> *controller = [self.childVcsDic valueForKey:[NSString stringWithFormat:@"%ld", index]];
    if (controller) {
        if ([controller respondsToSelector:@selector(zj_viewDidAppearForIndex:)]) {
            [controller zj_viewDidAppearForIndex:index];
        }
        if (_delegate && [_delegate respondsToSelector:@selector(scrollPageController:childViewControllDidAppear:forIndex:)]) {
            [_delegate scrollPageController:self.parentViewController childViewControllDidAppear:controller forIndex:index];
        }
    }
}
- (void)willDisappearWithIndex:(NSInteger)index {
    UIViewController<ZJScrollPageViewChildVcDelegate> *controller = [self.childVcsDic valueForKey:[NSString stringWithFormat:@"%ld", index]];
    if (controller) {
        if ([controller respondsToSelector:@selector(zj_viewWillDisappearForIndex:)]) {
            [controller zj_viewWillDisappearForIndex:index];
        }
        if (_delegate && [_delegate respondsToSelector:@selector(scrollPageController:childViewControllWillDisappear:forIndex:)]) {
            [_delegate scrollPageController:self.parentViewController childViewControllWillDisappear:controller forIndex:index];
        }
    }
}
- (void)didDisappearWithIndex:(NSInteger)index {
    UIViewController<ZJScrollPageViewChildVcDelegate> *controller = [self.childVcsDic valueForKey:[NSString stringWithFormat:@"%ld", index]];
    if (controller) {
        if ([controller respondsToSelector:@selector(zj_viewDidDisappearForIndex:)]) {
            [controller zj_viewDidDisappearForIndex:index];
        }
        if (_delegate && [_delegate respondsToSelector:@selector(scrollPageController:childViewControllDidDisappear:forIndex:)]) {
            [_delegate scrollPageController:self.parentViewController childViewControllDidDisappear:controller forIndex:index];
        }
    }
}
- (void)setCurrentIndex:(NSInteger)currentIndex andScrollDirection:(ZJScrollPageControllerScrollDirection)scrollDirection {
    if (currentIndex != _currentIndex) {
//        NSLog(@"current -- %ld   _current ---- %ld _oldIndex --- %ld", currentIndex, _currentIndex, _oldIndex);
        [self setupSubviewsWithCurrentIndex:currentIndex oldIndex:_oldIndex];
        if (scrollDirection != ZJScrollPageControllerScrollDirectionNone) {
            // 打开右边, 但是未松手又返回了打开左边
            // 打开左边, 但是未松手又返回了打开右边
            [self didDisappearWithIndex:_currentIndex];
        }
        [self willAppearWithIndex:currentIndex];
        [self willDisappearWithIndex:_oldIndex];
        if (_isLoadFirstView) { // 加载第一个controller的时候 同时出发didAppear
            [self didAppearWithIndex:currentIndex];
            _isLoadFirstView = NO;
        }
        _scrollDirection = scrollDirection;
        _currentIndex = currentIndex;
//        NSLog(@"%@",self.scrollView.subviews);
    }
}
- (void)setupSubviewsWithCurrentIndex:(NSInteger)currentIndex oldIndex:(NSInteger)oldIndex {
    UIViewController<ZJScrollPageViewChildVcDelegate> *currentController = [self.childVcsDic valueForKey:[NSString stringWithFormat:@"%ld", (long)currentIndex]];
    if (_delegate && [_delegate respondsToSelector:@selector(childViewController:forIndex:)]) {
        if (currentController == nil) {
            currentController = [_delegate childViewController:nil forIndex:currentIndex];
            [self.childVcsDic setValue:currentController forKey:[NSString stringWithFormat:@"%ld", (long)currentIndex]];
        } else {
            [_delegate childViewController:currentController forIndex:currentIndex];
        }
    } else {
        NSAssert(NO, @"必须设置代理和实现代理方法");
    }
    if ([currentController isKindOfClass:[UINavigationController class]]) {
        NSAssert(NO, @"不要添加UINavigationController包装后的子控制器");
    }
    CGFloat width = self.bounds.size.width;
    CGFloat height = self.bounds.size.height;
    // 添加currentController
    if (currentController.zj_scrollViewController != self.parentViewController) {
        [self.parentViewController addChildViewController:currentController];
    }
    [self.currentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    self.currentView.frame = CGRectMake(currentIndex*width, 0, width, height);
    [self.currentView addSubview:currentController.view];
    [_currentChildVc didMoveToParentViewController:self.parentViewController];
    UIViewController *oldController = [self.childVcsDic valueForKey:[NSString stringWithFormat:@"%ld", (long)_oldIndex]];
    // 添加oldController
    if (oldController) {
        [self.oldView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
        self.oldView.frame = CGRectMake(oldIndex*width, 0, width, height);
        [self.oldView addSubview:oldController.view];
        [oldController didMoveToParentViewController:self.parentViewController];
    }
    [self setNeedsLayout];
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (self.parentViewController.parentViewController && [self.parentViewController.parentViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController *navi = (UINavigationController *)self.parentViewController.parentViewController;
        if (navi.visibleViewController == self.parentViewController) {// 当显示的是ScrollPageView的时候 只在第一个tag处执行pop手势
            return self.scrollView.contentOffset.x == 0;
        } else {
            return [super gestureRecognizerShouldBegin:gestureRecognizer];
        }
    }
    return [super gestureRecognizerShouldBegin:gestureRecognizer];
}
#pragma mark - getter --- setter
- (UIScrollView *)scrollView {
    if (!_scrollView) {
        UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
        self.currentView = [[UIView alloc] init];
        self.oldView = [[UIView alloc] init];
        scrollView.delegate = self;
        [scrollView addSubview:self.currentView];
        [scrollView addSubview:self.oldView];
        scrollView.pagingEnabled = YES;
        scrollView.showsVerticalScrollIndicator = NO;
        scrollView.showsHorizontalScrollIndicator = NO;
        scrollView.bounces = self.segmentView.segmentStyle.isContentViewBounces;
        scrollView.scrollEnabled = self.segmentView.segmentStyle.isScrollContentView;
        scrollView.contentSize = CGSizeMake(self.itemsCount*self.bounds.size.width, self.bounds.size.height);
        _scrollView = scrollView;
    }
    return _scrollView;
}
- (NSMutableDictionary<NSString *,UIViewController<ZJScrollPageViewChildVcDelegate> *> *)childVcsDic {
    if (!_childVcsDic) {
        NSMutableDictionary *dic = [NSMutableDictionary dictionary];
        _childVcsDic = dic;
    }
    return _childVcsDic;
}
@end