| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 | 
							- //
 
- //  JXPagerView.m
 
- //  JXPagerView
 
- //
 
- //  Created by jiaxin on 2018/8/27.
 
- //  Copyright © 2018年 jiaxin. All rights reserved.
 
- //
 
- #import "JXPagerView.h"
 
- @class JXPagerListContainerScrollView;
 
- @class JXPagerListContainerCollectionView;
 
- @interface JXPagerView () <UITableViewDataSource, UITableViewDelegate, JXPagerListContainerViewDelegate>
 
- @property (nonatomic, weak) id<JXPagerViewDelegate> delegate;
 
- @property (nonatomic, strong) JXPagerMainTableView *mainTableView;
 
- @property (nonatomic, strong) JXPagerListContainerView *listContainerView;
 
- @property (nonatomic, strong) UIScrollView *currentScrollingListView;
 
- @property (nonatomic, strong) id<JXPagerViewListViewDelegate> currentList;
 
- @property (nonatomic, strong) NSMutableDictionary <NSNumber *, id<JXPagerViewListViewDelegate>> *validListDict;
 
- @property (nonatomic, strong) UIView *tableHeaderContainerView;
 
- @property (nonatomic, strong) NSMutableDictionary<NSString *, id<JXPagerViewListViewDelegate>> *listCache;
 
- @end
 
- @implementation JXPagerView
 
- - (instancetype)initWithDelegate:(id<JXPagerViewDelegate>)delegate {
 
-     return [self initWithDelegate:delegate listContainerType:JXPagerListContainerType_CollectionView];
 
- }
 
- - (instancetype)initWithDelegate:(id<JXPagerViewDelegate>)delegate listContainerType:(JXPagerListContainerType)type {
 
-     self = [super initWithFrame:CGRectZero];
 
-     if (self) {
 
-         _delegate = delegate;
 
-         _validListDict = [NSMutableDictionary dictionary];
 
-         _automaticallyDisplayListVerticalScrollIndicator = YES;
 
-         _isListHorizontalScrollEnabled = YES;
 
-         _mainTableView = [[JXPagerMainTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
 
-         self.mainTableView.showsVerticalScrollIndicator = NO;
 
-         self.mainTableView.showsHorizontalScrollIndicator = NO;
 
-         self.mainTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
 
-         self.mainTableView.scrollsToTop = NO;
 
-         self.mainTableView.dataSource = self;
 
-         self.mainTableView.delegate = self;
 
-         [self refreshTableHeaderView];
 
-         [self.mainTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
 
-         if (@available(iOS 11.0, *)) {
 
-             self.mainTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
 
-         }
 
- #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
 
-         if (@available(iOS 15.0, *)) {
 
-             self.mainTableView.sectionHeaderTopPadding = 0;
 
-         }
 
- #endif
 
-         [self addSubview:self.mainTableView];
 
-         _listContainerView = [[JXPagerListContainerView alloc] initWithType:type delegate:self];
 
-     }
 
-     return self;
 
- }
 
- - (void)layoutSubviews {
 
-     [super layoutSubviews];
 
-     if (!CGRectEqualToRect(self.bounds, self.mainTableView.frame)) {
 
-         self.mainTableView.frame = self.bounds;
 
-         [self.mainTableView reloadData];
 
-     }
 
- }
 
- - (void)setDefaultSelectedIndex:(NSInteger)defaultSelectedIndex {
 
-     _defaultSelectedIndex = defaultSelectedIndex;
 
-     self.listContainerView.defaultSelectedIndex = defaultSelectedIndex;
 
- }
 
- - (void)setIsListHorizontalScrollEnabled:(BOOL)isListHorizontalScrollEnabled {
 
-     _isListHorizontalScrollEnabled = isListHorizontalScrollEnabled;
 
-     self.listContainerView.scrollView.scrollEnabled = isListHorizontalScrollEnabled;
 
- }
 
- - (void)reloadData {
 
-     self.currentList = nil;
 
-     self.currentScrollingListView = nil;
 
-     [_validListDict removeAllObjects];
 
-     //根据新数据删除不需要的list
 
-     if (self.allowsCacheList) {
 
-         NSMutableArray *newListIdentifierArray = [NSMutableArray array];
 
-         if (self.delegate && [self.delegate respondsToSelector:@selector(numberOfListsInPagerView:)]) {
 
-             NSInteger listCount = [self.delegate numberOfListsInPagerView:self];
 
-             for (NSInteger index = 0; index < listCount; index ++) {
 
-                 if (self.delegate && [self.delegate respondsToSelector:@selector(pagerView:listIdentifierAtIndex:)]) {
 
-                     NSString *listIdentifier = [self.delegate pagerView:self listIdentifierAtIndex:index];
 
-                     [newListIdentifierArray addObject:listIdentifier];
 
-                 }
 
-             }
 
-         }
 
-         NSArray *existedKeys = self.listCache.allKeys;
 
-         for (NSString *listIdentifier in existedKeys) {
 
-             if (![newListIdentifierArray containsObject:listIdentifier]) {
 
-                 [self.listCache removeObjectForKey:listIdentifier];
 
-             }
 
-         }
 
-     }
 
-     [self refreshTableHeaderView];
 
-     if (self.pinSectionHeaderVerticalOffset != 0 && self.mainTableView.contentOffset.y > self.pinSectionHeaderVerticalOffset) {
 
-         self.mainTableView.contentOffset = CGPointZero;
 
-     }
 
-     [self.mainTableView reloadData];
 
-     [self.listContainerView reloadData];
 
- }
 
- - (void)resizeTableHeaderViewHeightWithAnimatable:(BOOL)animatable duration:(NSTimeInterval)duration curve:(UIViewAnimationCurve)curve {
 
-     if (animatable) {
 
-         UIViewAnimationOptions options = UIViewAnimationOptionCurveLinear;
 
-         switch (curve) {
 
-             case UIViewAnimationCurveEaseIn: options = UIViewAnimationOptionCurveEaseIn; break;
 
-             case UIViewAnimationCurveEaseOut: options = UIViewAnimationOptionCurveEaseOut; break;
 
-             case UIViewAnimationCurveEaseInOut: options = UIViewAnimationOptionCurveEaseInOut; break;
 
-             default: break;
 
-         }
 
-         [UIView animateWithDuration:duration delay:0 options:options animations:^{
 
-             CGRect frame = self.tableHeaderContainerView.bounds;
 
-             frame.size.height = [self.delegate tableHeaderViewHeightInPagerView:self];
 
-             self.tableHeaderContainerView.frame = frame;
 
-             self.mainTableView.tableHeaderView = self.tableHeaderContainerView;
 
-             [self.mainTableView setNeedsLayout];
 
-             [self.mainTableView layoutIfNeeded];
 
-         } completion:^(BOOL finished) { }];
 
-     }else {
 
-         CGRect frame = self.tableHeaderContainerView.bounds;
 
-         frame.size.height = [self.delegate tableHeaderViewHeightInPagerView:self];
 
-         self.tableHeaderContainerView.frame = frame;
 
-         self.mainTableView.tableHeaderView = self.tableHeaderContainerView;
 
-     }
 
- }
 
- #pragma mark - Private
 
- - (void)refreshTableHeaderView {
 
-     UIView *tableHeaderView = [self.delegate tableHeaderViewInPagerView:self];
 
-     UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, [self.delegate tableHeaderViewHeightInPagerView:self])];
 
-     [containerView addSubview:tableHeaderView];
 
-     tableHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
 
-     NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:tableHeaderView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeTop multiplier:1 constant:0];
 
-     NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:tableHeaderView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
 
-     NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:tableHeaderView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
 
-     NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:tableHeaderView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
 
-     [containerView addConstraints:@[top, leading, bottom, trailing]];
 
-     self.tableHeaderContainerView = containerView;
 
-     self.mainTableView.tableHeaderView = containerView;
 
- }
 
- - (void)adjustMainScrollViewToTargetContentInsetIfNeeded:(UIEdgeInsets)insets {
 
-     if (UIEdgeInsetsEqualToEdgeInsets(insets, self.mainTableView.contentInset) == NO) {
 
-         self.mainTableView.delegate = nil;
 
-         self.mainTableView.contentInset = insets;
 
-         self.mainTableView.delegate = self;
 
-     }
 
- }
 
- - (void)listViewDidScroll:(UIScrollView *)scrollView {
 
-     self.currentScrollingListView = scrollView;
 
-     [self preferredProcessListViewDidScroll:scrollView];
 
- }
 
- //仅用于处理设置了pinSectionHeaderVerticalOffset,又添加了MJRefresh的下拉刷新。这种情况会导致JXPagingView和MJRefresh来回设置contentInset值。针对这种及其特殊的情况,就内部特殊处理了。通过下面的判断条件,来判定当前是否处于下拉刷新中。请勿让pinSectionHeaderVerticalOffset和下拉刷新设置的contentInset.top值相同。
 
- //具体原因参考:https://github.com/pujiaxin33/JXPagingView/issues/203
 
- - (BOOL)isSetMainScrollViewContentInsetToZeroEnabled:(UIScrollView *)scrollView {
 
-     //scrollView.contentInset.top不为0,且scrollView.contentInset.top不等于pinSectionHeaderVerticalOffset,即可认为列表正在刷新。所以这里必须要保证pinSectionHeaderVerticalOffset和MJRefresh的mj_insetT的值不相等。
 
-     BOOL isRefreshing = scrollView.contentInset.top != 0 && scrollView.contentInset.top != self.pinSectionHeaderVerticalOffset;
 
-     return !isRefreshing;
 
- }
 
- #pragma mark - UITableViewDataSource, UITableViewDelegate
 
- - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
 
-     return 1;
 
- }
 
- - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
 
-     return MAX(self.bounds.size.height - [self.delegate heightForPinSectionHeaderInPagerView:self] - self.pinSectionHeaderVerticalOffset, 0);
 
- }
 
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
-     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
 
-     cell.selectionStyle = UITableViewCellSelectionStyleNone;
 
-     cell.backgroundColor = [UIColor clearColor];
 
-     if (self.listContainerView.superview != cell.contentView) {
 
-         [cell.contentView addSubview:self.listContainerView];
 
-     }
 
-     if (!CGRectEqualToRect(self.listContainerView.frame, cell.bounds)) {
 
-         self.listContainerView.frame = cell.bounds;
 
-     }
 
-     return cell;
 
- }
 
- - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
 
-     return [self.delegate heightForPinSectionHeaderInPagerView:self];
 
- }
 
- - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
 
-     return [self.delegate viewForPinSectionHeaderInPagerView:self];
 
- }
 
- - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
 
-     return 1;
 
- }
 
- - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
 
-     UIView *footer = [[UIView alloc] initWithFrame:CGRectZero];
 
-     footer.backgroundColor = [UIColor clearColor];
 
-     return footer;
 
- }
 
- - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
 
-     if (self.pinSectionHeaderVerticalOffset != 0) {
 
-         if (!(self.currentScrollingListView != nil && self.currentScrollingListView.contentOffset.y > [self minContentOffsetYInListScrollView:self.currentScrollingListView])) {
 
-             //没有处于滚动某一个listView的状态
 
-             if (scrollView.contentOffset.y >= self.pinSectionHeaderVerticalOffset) {
 
-                 //固定的位置就是contentInset.top
 
-                 [self adjustMainScrollViewToTargetContentInsetIfNeeded:UIEdgeInsetsMake(self.pinSectionHeaderVerticalOffset, 0, 0, 0)];
 
-             }else {
 
-                 if ([self isSetMainScrollViewContentInsetToZeroEnabled:scrollView]) {
 
-                     [self adjustMainScrollViewToTargetContentInsetIfNeeded:UIEdgeInsetsZero];
 
-                 }
 
-             }
 
-         }
 
-     }
 
-     [self preferredProcessMainTableViewDidScroll:scrollView];
 
-     if (self.delegate && [self.delegate respondsToSelector:@selector(mainTableViewDidScroll:)]) {
 
-         #pragma GCC diagnostic push
 
-         #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 
-         [self.delegate mainTableViewDidScroll:scrollView];
 
-         #pragma GCC diagnostic pop
 
-     }
 
-     if (self.delegate && [self.delegate respondsToSelector:@selector(pagerView:mainTableViewDidScroll:)]) {
 
-         [self.delegate pagerView:self mainTableViewDidScroll:scrollView];
 
-     }
 
- }
 
- - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
 
-     self.listContainerView.scrollView.scrollEnabled = NO;
 
-     if (self.delegate && [self.delegate respondsToSelector:@selector(pagerView:mainTableViewWillBeginDragging:)]) {
 
-         [self.delegate pagerView:self mainTableViewWillBeginDragging:scrollView];
 
-     }
 
- }
 
- - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
 
-     if (self.isListHorizontalScrollEnabled && !decelerate) {
 
-         self.listContainerView.scrollView.scrollEnabled = YES;
 
-     }
 
-     if (self.delegate && [self.delegate respondsToSelector:@selector(pagerView:mainTableViewDidEndDragging:willDecelerate:)]) {
 
-         [self.delegate pagerView:self mainTableViewDidEndDragging:scrollView willDecelerate:decelerate];
 
-     }
 
- }
 
- - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
 
-     if (self.isListHorizontalScrollEnabled) {
 
-         self.listContainerView.scrollView.scrollEnabled = YES;
 
-     }
 
-     if ([self isSetMainScrollViewContentInsetToZeroEnabled:scrollView]) {
 
-         if (self.mainTableView.contentInset.top != 0 && self.pinSectionHeaderVerticalOffset != 0) {
 
-             [self adjustMainScrollViewToTargetContentInsetIfNeeded:UIEdgeInsetsZero];
 
-         }
 
-     }
 
-     if (self.delegate && [self.delegate respondsToSelector:@selector(pagerView:mainTableViewDidEndDecelerating:)]) {
 
-         [self.delegate pagerView:self mainTableViewDidEndDecelerating:scrollView];
 
-     }
 
- }
 
- - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
 
-     if (self.isListHorizontalScrollEnabled) {
 
-         self.listContainerView.scrollView.scrollEnabled = YES;
 
-     }
 
-     if (self.delegate && [self.delegate respondsToSelector:@selector(pagerView:mainTableViewDidEndScrollingAnimation:)]) {
 
-         [self.delegate pagerView:self mainTableViewDidEndScrollingAnimation:scrollView];
 
-     }
 
- }
 
- #pragma mark - JXPagerListContainerViewDelegate
 
- - (NSInteger)numberOfListsInlistContainerView:(JXPagerListContainerView *)listContainerView {
 
-     return [self.delegate numberOfListsInPagerView:self];
 
- }
 
- - (id<JXPagerViewListViewDelegate>)listContainerView:(JXPagerListContainerView *)listContainerView initListForIndex:(NSInteger)index {
 
-     id<JXPagerViewListViewDelegate> list = self.validListDict[@(index)];
 
-     if (list == nil) {
 
-         if (self.allowsCacheList && self.delegate && [self.delegate respondsToSelector:@selector(pagerView:listIdentifierAtIndex:)]) {
 
-             NSString *listIdentifier = [self.delegate pagerView:self listIdentifierAtIndex:index];
 
-             list = self.listCache[listIdentifier];
 
-         }
 
-     }
 
-     if (list == nil) {
 
-         list = [self.delegate pagerView:self initListAtIndex:index];
 
-         __weak typeof(self)weakSelf = self;
 
-         __weak typeof(id<JXPagerViewListViewDelegate>) weakList = list;
 
-         [list listViewDidScrollCallback:^(UIScrollView *scrollView) {
 
-             weakSelf.currentList = weakList;
 
-             [weakSelf listViewDidScroll:scrollView];
 
-         }];
 
-         _validListDict[@(index)] = list;
 
-         if (self.allowsCacheList && self.delegate && [self.delegate respondsToSelector:@selector(pagerView:listIdentifierAtIndex:)]) {
 
-             NSString *listIdentifier = [self.delegate pagerView:self listIdentifierAtIndex:index];
 
-             self.listCache[listIdentifier] = list;
 
-         }
 
-     }
 
-     return list;
 
- }
 
- - (void)listContainerViewWillBeginDragging:(JXPagerListContainerView *)listContainerView {
 
-     self.mainTableView.scrollEnabled = NO;
 
- }
 
- - (void)listContainerViewWDidEndScroll:(JXPagerListContainerView *)listContainerView {
 
-     self.mainTableView.scrollEnabled = YES;
 
- }
 
- - (void)listContainerView:(JXPagerListContainerView *)listContainerView listDidAppearAtIndex:(NSInteger)index {
 
-     self.currentScrollingListView = [self.validListDict[@(index)] listScrollView];
 
-     for (id<JXPagerViewListViewDelegate> listItem in self.validListDict.allValues) {
 
-         if (listItem == self.validListDict[@(index)]) {
 
-             [listItem listScrollView].scrollsToTop = YES;
 
-         }else {
 
-             [listItem listScrollView].scrollsToTop = NO;
 
-         }
 
-     }
 
- }
 
- - (Class)scrollViewClassInlistContainerView:(JXPagerListContainerView *)listContainerView {
 
-     if (self.delegate && [self.delegate respondsToSelector:@selector(scrollViewClassInlistContainerViewInPagerView:)]) {
 
-         return [self.delegate scrollViewClassInlistContainerViewInPagerView:self];
 
-     }
 
-     return nil;
 
- }
 
- @end
 
- @implementation JXPagerView (UISubclassingGet)
 
- - (CGFloat)mainTableViewMaxContentOffsetY {
 
-     return [self.delegate tableHeaderViewHeightInPagerView:self] - self.pinSectionHeaderVerticalOffset;
 
- }
 
- @end
 
- @implementation JXPagerView (UISubclassingHooks)
 
- - (void)preferredProcessListViewDidScroll:(UIScrollView *)scrollView {
 
-     if (self.mainTableView.contentOffset.y < self.mainTableViewMaxContentOffsetY) {
 
-         //mainTableView的header还没有消失,让listScrollView一直为0
 
-         if (self.currentList && [self.currentList respondsToSelector:@selector(listScrollViewWillResetContentOffset)]) {
 
-             [self.currentList listScrollViewWillResetContentOffset];
 
-         }
 
-         [self setListScrollViewToMinContentOffsetY:scrollView];
 
-         if (self.automaticallyDisplayListVerticalScrollIndicator) {
 
-             scrollView.showsVerticalScrollIndicator = NO;
 
-         }
 
-     }else {
 
-         //mainTableView的header刚好消失,固定mainTableView的位置,显示listScrollView的滚动条
 
-         self.mainTableView.contentOffset = CGPointMake(0, self.mainTableViewMaxContentOffsetY);
 
-         if (self.automaticallyDisplayListVerticalScrollIndicator) {
 
-             scrollView.showsVerticalScrollIndicator = YES;
 
-         }
 
-     }
 
- }
 
- - (void)preferredProcessMainTableViewDidScroll:(UIScrollView *)scrollView {
 
-     if (self.currentScrollingListView != nil && self.currentScrollingListView.contentOffset.y > [self minContentOffsetYInListScrollView:self.currentScrollingListView]) {
 
-         //mainTableView的header已经滚动不见,开始滚动某一个listView,那么固定mainTableView的contentOffset,让其不动
 
-         [self setMainTableViewToMaxContentOffsetY];
 
-     }
 
-     if (scrollView.contentOffset.y < self.mainTableViewMaxContentOffsetY) {
 
-         //mainTableView已经显示了header,listView的contentOffset需要重置
 
-         for (id<JXPagerViewListViewDelegate> list in self.validListDict.allValues) {
 
-             if ([list respondsToSelector:@selector(listScrollViewWillResetContentOffset)]) {
 
-                 [list listScrollViewWillResetContentOffset];
 
-             }
 
-             [self setListScrollViewToMinContentOffsetY:[list listScrollView]];
 
-         }
 
-     }
 
-     if (scrollView.contentOffset.y > self.mainTableViewMaxContentOffsetY && self.currentScrollingListView.contentOffset.y == [self minContentOffsetYInListScrollView:self.currentScrollingListView]) {
 
-         //当往上滚动mainTableView的headerView时,滚动到底时,修复listView往上小幅度滚动
 
-         [self setMainTableViewToMaxContentOffsetY];
 
-     }
 
- }
 
- - (void)setMainTableViewToMaxContentOffsetY {
 
-     self.mainTableView.contentOffset = CGPointMake(0, self.mainTableViewMaxContentOffsetY);
 
- }
 
- - (void)setListScrollViewToMinContentOffsetY:(UIScrollView *)scrollView {
 
-     scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, [self minContentOffsetYInListScrollView:scrollView]);
 
- }
 
- - (CGFloat)minContentOffsetYInListScrollView:(UIScrollView *)scrollView {
 
-     if (@available(iOS 11.0, *)) {
 
-         return -scrollView.adjustedContentInset.top;
 
-     }
 
-     return -scrollView.contentInset.top;
 
- }
 
- @end
 
 
  |