提交
This commit is contained in:
21
Pods/JXPagingView/LICENSE
generated
Normal file
21
Pods/JXPagingView/LICENSE
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 暴走的鑫鑫
|
||||
|
||||
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.
|
||||
294
Pods/JXPagingView/README.md
generated
Normal file
294
Pods/JXPagingView/README.md
generated
Normal file
@@ -0,0 +1,294 @@
|
||||
# JXPagingView
|
||||
|
||||
类似微博主页、简书主页、QQ联系人页面等效果。多页面嵌套,既可以上下滑动,也可以左右滑动切换页面。支持HeaderView悬浮、支持下拉刷新、上拉加载更多。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- 支持OC与Swift;
|
||||
- 支持列表懒加载,等到列表真正显示的时候才加载,而不是一次性加载所有列表;
|
||||
- 支持首页下拉刷新、列表视图下拉刷新、列表视图上拉加载更多;
|
||||
- 支持悬浮SectionHeader的垂直位置调整;
|
||||
- 支持从顶部用力往上滚动,下面的列表会跟着滚动,而不会突然卡主,需要使用`JXPagerSmoothView`类;
|
||||
- 列表封装简洁,只要遵从`JXPagingViewListViewDelegate`协议即可。UIView、UIViewController等都可以;
|
||||
- 使用JXCategoryView/JXSegmentedView分类控制器,几乎支持所有主流效果、高度自定义、可灵活扩展;
|
||||
- 支持横竖屏切换;
|
||||
- 支持点击状态栏滚动当前列表到顶部;
|
||||
- 支持列表显示和消失的生命周期方法;
|
||||
- isListHorizontalScrollEnabled属性控制列表是否可以左右滑动,默认YES;
|
||||
- 支持`FDFullscreenPopGesture`等全屏手势兼容处理;
|
||||
|
||||
## 预览
|
||||
|
||||
| 效果 | 预览图 |
|
||||
|-------|-------|
|
||||
| **头图缩放** <br/>参考[ZoomViewController](https://github.com/pujiaxin33/JXPagingView/tree/master/Examples/JXPagerViewExample-OC/JXPagerViewExample-OC/Example/Zoom/ZoomViewController.m)类 |  |
|
||||
| **主页下拉刷新&列表上拉加载更多** <br/>参考[RefreshViewController](https://github.com/pujiaxin33/JXPagingView/tree/master/Examples/JXPagerViewExample-OC/JXPagerViewExample-OC/Example/Refresh/RefreshViewController.m)类 |  |
|
||||
| **列表下拉刷新** <br/>参考[ListRefreshViewController](https://github.com/pujiaxin33/JXPagingView/tree/master/Examples/JXPagerViewExample-OC/JXPagerViewExample-OC/Example/Refresh/ListRefreshViewController.m)类 |  |
|
||||
| **悬浮sectionHeader位置调整** |  |
|
||||
| **导航栏隐藏** <br/> 参考[NaviBarHiddenViewController](https://github.com/pujiaxin33/JXPagingView/tree/master/Examples/JXPagerViewExample-OC/JXPagerViewExample-OC/Example/NavigationBarHidden/NaviBarHiddenViewController.m)类 |  |
|
||||
| **CollectionView列表示例**<br/>参考[CollectionViewViewController.swift](https://github.com/pujiaxin33/JXPagingView/tree/master/Examples/JXPagingViewExample/JXPagingViewExample/Example/CollectionView/CollectionViewViewController.swift)类 <br/> 只有swift的demo工程有该示例 |  |
|
||||
| **HeaderView更新高度示例**<br/> 参考[HeightChangeAnimationViewController.swift](https://github.com/pujiaxin33/JXPagingView/tree/master/Examples/JXPagingViewExample/JXPagingViewExample/Example/HeightChange/HeightChangeAnimationViewController.swift)类 <br/> 只有swift demo工程才有该示例 |  |
|
||||
| **PagingView嵌套CategoryView** <br/> 参考[NestViewController](https://github.com/pujiaxin33/JXPagingView/tree/master/Examples/JXPagerViewExample-OC/JXPagerViewExample-OC/Example/Nest/NestViewController.m)类 <br/> 只有 **OC!OC!OC!** 的demo工程才有该示例 <br/> 操作比较特殊,如果需要此效果,<br/> 请认真参考源码,有问题多试试 <br/> 参考NestViewController.h类 |  |
|
||||
| **CategoryView嵌套PagingView** <br/> 参考[NestViewController.swift](https://github.com/pujiaxin33/JXPagingView/tree/master/Examples/JXPagingViewExample/JXPagingViewExample/Example/CategoryNestPaging/NestViewController.swift)类 <br/> 只有 **Swift!Swift!Swift!** 的demo工程才有该示例 <br/> 操作比较特殊,如果需要此效果,<br/> 请认真参考源码,有问题多试试 <br/> 参考NestViewController.swift类 |  |
|
||||
| **点击状态栏** |  |
|
||||
| **横竖屏旋转** |  |
|
||||
| **JXPageListView**<br/> 顶部需要自定义cell的场景,类似于电商APP首页,滑动到列表最底部才是分类控制器 <br/> 该效果是另一个库,点击查看[JXPageListView](https://github.com/pujiaxin33/JXPageListView) <br/> 该效果是另一个库,点击查看[JXPageListView](https://github.com/pujiaxin33/JXPageListView) <br/> 该效果是另一个库,点击查看[JXPageListView](https://github.com/pujiaxin33/JXPageListView) |  |
|
||||
| **JXPagerSmoothView**<br/> 类似淘宝、转转首页 <br/> 从顶部用力往上滚动,下面的列表会继续滚动 |  |
|
||||
|
||||
## 安装
|
||||
|
||||
### 手动
|
||||
|
||||
**Swift版本:** Clone代码,拖入JXPagingView-Swift文件夹,使用`JXPagingView`类;
|
||||
|
||||
**OC版本:** Clone代码,拖入JXPagerView文件夹,使用`JXPagerView`类;
|
||||
|
||||
### CocoaPods
|
||||
|
||||
- **Swift版本**
|
||||
|
||||
支持swift版本:5.0+
|
||||
|
||||
```ruby
|
||||
target '<Your Target Name>' do
|
||||
pod 'JXPagingView/Paging'
|
||||
end
|
||||
```
|
||||
|
||||
- **OC版本**
|
||||
```ruby
|
||||
target '<Your Target Name>' do
|
||||
pod 'JXPagingView/Pager'
|
||||
end
|
||||
```
|
||||
|
||||
Swift与OC的仓库地址不一样,请注意选择!
|
||||
|
||||
先`pod repo update`然后再`pod install`
|
||||
|
||||
|
||||
## 使用
|
||||
|
||||
swift版本使用类似,只是类名及相关API更改为`JXPagingView`,具体细节请查看Swfit工程。
|
||||
|
||||
### 1、初始化`JXCategoryTitleView`和`JXPagerView`
|
||||
|
||||
```Objective-C
|
||||
self.categoryView = [[JXCategoryTitleView alloc] initWithFrame:frame];
|
||||
//配置categoryView,细节参考源码
|
||||
|
||||
self.pagerView = [[JXPagerView alloc] initWithDelegate:self];
|
||||
[self.view addSubview:self.pagerView];
|
||||
|
||||
//⚠️⚠️⚠️将pagerView的listContainerView和categoryView.listContainer进行关联,这样列表就可以和categoryView联动了。⚠️⚠️⚠️
|
||||
self.categoryView.listContainer = (id<JXCategoryViewListContainer>)self.pagerView.listContainerView;
|
||||
```
|
||||
|
||||
**Swift版本列表关联代码**
|
||||
```Swift
|
||||
//给JXPagingListContainerView添加extension,表示遵从JXSegmentedViewListContainer的协议
|
||||
extension JXPagingListContainerView: JXSegmentedViewListContainer {}
|
||||
//⚠️⚠️⚠️将pagingView的listContainerView和segmentedView.listContainer进行关联,这样列表就可以和categoryView联动了。⚠️⚠️⚠️
|
||||
segmentedView.listContainer = pagingView.listContainerView
|
||||
```
|
||||
|
||||
### 2、实现`JXPagerViewDelegate`协议
|
||||
|
||||
```Objective-C
|
||||
/**
|
||||
返回tableHeaderView的高度,因为内部需要比对判断,只能是整型数
|
||||
*/
|
||||
- (NSUInteger)tableHeaderViewHeightInPagerView:(JXPagerView *)pagerView {
|
||||
return JXTableHeaderViewHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
返回tableHeaderView
|
||||
*/
|
||||
- (UIView *)tableHeaderViewInPagerView:(JXPagerView *)pagerView {
|
||||
return self.userHeaderView;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
返回悬浮HeaderView的高度,因为内部需要比对判断,只能是整型数
|
||||
*/
|
||||
- (NSUInteger)heightForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
|
||||
return JXheightForHeaderInSection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
返回悬浮HeaderView
|
||||
*/
|
||||
- (UIView *)viewForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
|
||||
return self.categoryView;
|
||||
}
|
||||
|
||||
/**
|
||||
返回列表的数量
|
||||
*/
|
||||
- (NSInteger)numberOfListsInPagerView:(JXPagerView *)pagerView {
|
||||
//和categoryView的item数量一致
|
||||
return self.titles.count;
|
||||
}
|
||||
|
||||
/**
|
||||
根据index初始化一个对应列表实例。注意:一定要是新生成的实例!!!
|
||||
只要遵循JXPagerViewListViewDelegate即可,无论你返回的是UIView还是UIViewController都可以。
|
||||
*/
|
||||
- (id<JXPagerViewListViewDelegate>)pagerView:(JXPagerView *)pagerView initListAtIndex:(NSInteger)index {
|
||||
TestListBaseView *listView = [[TestListBaseView alloc] init];
|
||||
if (index == 0) {
|
||||
listView.dataSource = @[@"橡胶火箭", @"橡胶火箭炮", @"橡胶机关枪"...].mutableCopy;
|
||||
}else if (index == 1) {
|
||||
listView.dataSource = @[@"吃烤肉", @"吃鸡腿肉", @"吃牛肉", @"各种肉"].mutableCopy;
|
||||
}else {
|
||||
listView.dataSource = @[@"【剑士】罗罗诺亚·索隆", @"【航海士】娜美", @"【狙击手】乌索普"...].mutableCopy;
|
||||
}
|
||||
[listView beginFirstRefresh];
|
||||
return listView;
|
||||
}
|
||||
```
|
||||
|
||||
### 3、实现`JXPagerViewListViewDelegate`协议
|
||||
|
||||
列表可以是任意类,UIView、UIViewController等等都可以,只要实现了`JXPagerViewListViewDelegate`协议就行。
|
||||
|
||||
⚠️⚠️⚠️一定要保证`scrollCallback`的正确回调,许多朋友都容易疏忽这一点,导致异常,务必重点注意!
|
||||
|
||||
下面的使用代码参考的是`TestListBaseView`类
|
||||
|
||||
```Objective-C
|
||||
/**
|
||||
返回listView。如果是vc包裹的就是vc.view;如果是自定义view包裹的,就是自定义view自己。
|
||||
*/
|
||||
- (UIView *)listView {
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
返回listView内部持有的UIScrollView或UITableView或UICollectionView
|
||||
主要用于mainTableView已经显示了header,listView的contentOffset需要重置时,内部需要访问到外部传入进来的listView内的scrollView
|
||||
*/
|
||||
- (UIScrollView *)listScrollView {
|
||||
return self.tableView;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
当listView内部持有的UIScrollView或UITableView或UICollectionView的代理方法`scrollViewDidScroll`回调时,需要调用该代理方法传入的callback
|
||||
*/
|
||||
- (void)listViewDidScrollCallback:(void (^)(UIScrollView *))callback {
|
||||
self.scrollCallback = callback;
|
||||
}
|
||||
```
|
||||
|
||||
### 4、列表回调处理
|
||||
|
||||
`TestListBaseView`在其`tableView`的滚动回调中,通过调用上面持有的scrollCallback,把列表的滚动事件回调给JXPagerView内部。
|
||||
```Objective-C
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
!self.scrollCallback ?: self.scrollCallback(scrollView);
|
||||
}
|
||||
```
|
||||
|
||||
## 实现原理
|
||||
|
||||
[实现原理](https://github.com/pujiaxin33/JXPagingView/blob/master/Document/JXPagingView%E5%8E%9F%E7%90%86.md)
|
||||
|
||||
## `JXPagerSmoothView`
|
||||
|
||||
如果你需要类似于**淘宝**、**转转**首页从顶部header用力往上滚动之后,下面的列表会跟着滚动的效果。因为`JXPagerView`的实现原理限制,当用户从顶部header的位置用力往上滚动,`JXPagerView`会在`JXCategoryView`刚好在顶部的时候突然停住。这个时候就需要使用`JXPagerSmoothView`,swift版本叫`JXPagingSmoothView`。
|
||||
|
||||
因为与`JXPagerView`的原理完全不同,所以各自会有一些特性的区别,但是从使用体验来说,是完全一致的。具体使用细节请参考demo示例。
|
||||
|
||||
实现原理参考[JXPagerSmoothView文章解析](https://juejin.im/post/5ddb2fe4f265da7def5424c7)
|
||||
|
||||
|
||||
## 特殊说明
|
||||
|
||||
### JXCategoryView、JXSegmentedView
|
||||
悬浮的HeaderView,用的是我写的:[OC版本-JXCategoryView](https://github.com/pujiaxin33/JXCategoryView) 、[Swift版本-JXSegmentedView](https://github.com/pujiaxin33/JXSegmentedView)。几乎实现了所有主流效果,而且非常容易自定义扩展,强烈推荐阅读。
|
||||
|
||||
|
||||
### 头图缩放说明
|
||||
头图缩放原理,参考这个库:[JXTableViewZoomHeaderImageView](https://github.com/pujiaxin33/JXTableViewZoomHeaderImageView)
|
||||
|
||||
### 列表下拉刷新说明
|
||||
|
||||
需要使用`JXPagerListRefreshView`类(是`JXPagerView`的子类)
|
||||
|
||||
### JXPagerListContainerType说明
|
||||
|
||||
UIScrollView:优势:没有其他副作用。劣势:实时的视图内存占用相对大一点,因为所有加载之后的列表视图都在视图层级里面。
|
||||
UICollectionView:优势:因为列表被添加到cell上,实时的视图内存占用更少,适合内存要求特别高的场景。劣势:因为cell重用机制的问题,导致列表被移除屏幕外之后,会被放入缓存区,而不存在于视图层级中。如果刚好你的列表使用了下拉刷新视图,在快速切换过程中,就会导致下拉刷新回调不成功的问题。(使用MJRefresh会出现此问题)一句话概括:使用CollectionView的时候,就不要让列表使用下拉刷新加载。
|
||||
|
||||
### 关于下方列表视图的代理方法`- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath`有时候需要点击两次才回调
|
||||
|
||||
出现步骤:当手指放在下方列表视图往下拉,直到TableHeaderView完全显示。
|
||||
|
||||
原因:经过上面的步骤之后,手指已经离开屏幕且列表视图已经完全静止,UIScrollView的isDragging属性却依然是true。就导致了后续的第一次点击,让系统认为当前UIScrollView依然在滚动,该点击就让UIScrollView停止下来,没有继续转发给UITableView,就没有转化成didSelectRow事件。
|
||||
|
||||
解决方案:经过N种尝试之后,还是没有回避掉系统的`isDragging`异常为true的bug。大家可以在自定义cell最下方放置一个与cell同大小的button,把button的touchUpInside事件当做`didSelectRow`的回调。因为UIButton在响应链中的优先级要高于UIGestureRecognizer。
|
||||
|
||||
代码:请参考`TestTableViewCell`类的配置。
|
||||
|
||||
### 指定默认选中index
|
||||
|
||||
默认显示index=2的列表,代码如下:
|
||||
```
|
||||
self.pagerView.defaultSelectedIndex = 2;
|
||||
self.categoryView.defaultSelectedIndex = 2;
|
||||
```
|
||||
|
||||
### 顶部轮播图手势处理
|
||||
|
||||
如果TableHeaderView添加了轮播图,获取其他可以横向滚动的UIScrollView。如果不处理,就会出现左右滚动轮播图的时候又可以触发整个页面的上下滚动。为了规避该问题,请参考示例仓库中`BannerViewController`类的处理方法。即可同一时间只允许左右滚动或者上下滚动。
|
||||
|
||||
### 关于列表用UIViewController封装且要支持横竖屏的tips
|
||||
|
||||
在列表UIViewController类里面一定要加上下面这段代码:(不要问我为什么,我也不知道,谁知道系统内部是怎么操作的,反正加上就没毛病了)
|
||||
```
|
||||
- (void)loadView {
|
||||
self.view = [[UIView alloc] init];
|
||||
}
|
||||
```
|
||||
|
||||
### `JXPagerSmoothView` header有UITextField或者`UITextView`
|
||||
详情参考OC版本示例【滚动延续 Header有输入框】
|
||||
|
||||
列表自定义子类化`UITableView`或者`UICollectionView`,然后重载`scrollRectToVisible`方法,示例代码如下。
|
||||
```Object-C
|
||||
@implementation TestTableView
|
||||
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated {
|
||||
[self setContentOffset:CGPointMake(self.contentOffset.x, rect.origin.y) animated:animated];
|
||||
}
|
||||
@end
|
||||
```
|
||||
|
||||
### `FDFullscreenPopGesture`等全屏手势兼容处理
|
||||
|
||||
[全屏手势兼容处理文档,点击查看 ❗️❗️❗️](https://github.com/pujiaxin33/JXPagingView/blob/master/Document/%E5%85%A8%E5%B1%8F%E6%89%8B%E5%8A%BF%E5%A4%84%E7%90%86.md)
|
||||
|
||||
|
||||
## 迁移指南
|
||||
- **0.0.9版本**:将下面两个API的返回值修改为了NSUInteger(swift版本为Int),之前版本是CGFloat,升级为0.0.9及以上的时候,记得修改一下使用地方的返回值类型,不然会引起crash。
|
||||
- `- (NSUInteger)heightForPinSectionHeaderInPagerView:(JXPagerView *)pagerView`
|
||||
- `- (NSUInteger)tableHeaderViewHeightInPagerView:(JXPagerView *)pagerView`
|
||||
- **1.0.0版本**:
|
||||
删除代理方法`- (NSArray <id<JXPagerViewListViewDelegate>> *)listViewsInPagerView:(JXPagerView *)pagerView;`,请参考示例使用下面两个代理方法:
|
||||
- `- (NSInteger)numberOfListsInPagerView:(JXPagerView *)pagerView;`
|
||||
- `- (id<JXPagerViewListViewDelegate>)pagerView:(JXPagerView *)pagerView initListAtIndex:(NSInteger)index;`
|
||||
- **2.0.0版本**:`JXPagerListContainerView`进行了重构,列表拥有了完整的生命周期方法。列表是`UIViewController`类,`viewWillAppear`等生命周期方法将会正确触发。
|
||||
-
|
||||
- 删除了collectionView,用`scrollView`属性替换。
|
||||
- 和`CategoryView`的联动绑定代码更新为`self.categoryView.listContainer = (id<JXCategoryViewListContainer>)self.pagerView.listContainerView;`。
|
||||
- `JXPagerView`新增`- (instancetype)initWithDelegate:(id<JXPagerViewDelegate>)delegate listContainerType:(JXPagerListContainerType)type`初始化方法,可以指定列表容器为`UIScrollView`或者`UICollectionView`;
|
||||
|
||||
|
||||
## 补充
|
||||
|
||||
有不明白的地方,建议多看下源码。再有疑问的,欢迎提Issue交流🤝
|
||||
|
||||
|
||||
128
Pods/JXPagingView/Sources/JXPagerView/JXPagerListContainerView.h
generated
Normal file
128
Pods/JXPagingView/Sources/JXPagerView/JXPagerListContainerView.h
generated
Normal file
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// JXCategoryListScrollView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/9/12.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@class JXPagerListContainerView;
|
||||
@class JXPagerListContainerScrollView;
|
||||
|
||||
@protocol JXPagerViewListViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
返回listView。如果是vc包裹的就是vc.view;如果是自定义view包裹的,就是自定义view自己。
|
||||
|
||||
@return UIView
|
||||
*/
|
||||
- (UIView *)listView;
|
||||
|
||||
/**
|
||||
返回listView内部持有的UIScrollView或UITableView或UICollectionView
|
||||
主要用于mainTableView已经显示了header,listView的contentOffset需要重置时,内部需要访问到外部传入进来的listView内的scrollView
|
||||
|
||||
@return listView内部持有的UIScrollView或UITableView或UICollectionView
|
||||
*/
|
||||
- (UIScrollView *)listScrollView;
|
||||
|
||||
/**
|
||||
当listView内部持有的UIScrollView或UITableView或UICollectionView的代理方法`scrollViewDidScroll`回调时,需要调用该代理方法传入的callback
|
||||
|
||||
@param callback `scrollViewDidScroll`回调时调用的callback
|
||||
*/
|
||||
- (void)listViewDidScrollCallback:(void (^)(UIScrollView *scrollView))callback;
|
||||
|
||||
@optional
|
||||
|
||||
- (void)listScrollViewWillResetContentOffset;
|
||||
- (void)listWillAppear;
|
||||
- (void)listDidAppear;
|
||||
- (void)listWillDisappear;
|
||||
- (void)listDidDisappear;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
列表容器视图的类型
|
||||
|
||||
- ScrollView: UIScrollView。优势:没有其他副作用。劣势:实时的视图内存占用相对大一点,因为所有加载之后的列表视图都在视图层级里面。
|
||||
- CollectionView: 使用UICollectionView。优势:因为列表被添加到cell上,实时的视图内存占用更少,适合内存要求特别高的场景。劣势:因为cell重用机制的问题,导致列表被移除屏幕外之后,会被放入缓存区,而不存在于视图层级中。如果刚好你的列表使用了下拉刷新视图,在快速切换过程中,就会导致下拉刷新回调不成功的问题。一句话概括:使用CollectionView的时候,就不要让列表使用下拉刷新加载。
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, JXPagerListContainerType) {
|
||||
JXPagerListContainerType_ScrollView,
|
||||
JXPagerListContainerType_CollectionView,
|
||||
};
|
||||
|
||||
@protocol JXPagerListContainerViewDelegate <NSObject>
|
||||
/**
|
||||
返回list的数量
|
||||
|
||||
@param listContainerView 列表的容器视图
|
||||
@return list的数量
|
||||
*/
|
||||
- (NSInteger)numberOfListsInlistContainerView:(JXPagerListContainerView *)listContainerView;
|
||||
|
||||
/**
|
||||
根据index返回一个对应列表实例,需要是遵从`JXPagerViewListViewDelegate`协议的对象。
|
||||
你可以代理方法调用的时候初始化对应列表,达到懒加载的效果。这也是默认推荐的初始化列表方法。你也可以提前创建好列表,等该代理方法回调的时候再返回也可以,达到预加载的效果。
|
||||
如果列表是用自定义UIView封装的,就让自定义UIView遵从`JXPagerViewListViewDelegate`协议,该方法返回自定义UIView即可。
|
||||
如果列表是用自定义UIViewController封装的,就让自定义UIViewController遵从`JXPagerViewListViewDelegate`协议,该方法返回自定义UIViewController即可。
|
||||
|
||||
@param listContainerView 列表的容器视图
|
||||
@param index 目标下标
|
||||
@return 遵从JXPagerViewListViewDelegate协议的list实例
|
||||
*/
|
||||
- (id<JXPagerViewListViewDelegate>)listContainerView:(JXPagerListContainerView *)listContainerView initListForIndex:(NSInteger)index;
|
||||
|
||||
@optional
|
||||
/**
|
||||
返回自定义UIScrollView或UICollectionView的Class
|
||||
某些特殊情况需要自己处理UIScrollView内部逻辑。比如项目用了FDFullscreenPopGesture,需要处理手势相关代理。
|
||||
|
||||
@param listContainerView JXPagerListContainerView
|
||||
@return 自定义UIScrollView实例
|
||||
*/
|
||||
- (Class)scrollViewClassInlistContainerView:(JXPagerListContainerView *)listContainerView;
|
||||
|
||||
/**
|
||||
控制能否初始化对应index的列表。有些业务需求,需要在某些情况才允许初始化某些列表,通过通过该代理实现控制。
|
||||
*/
|
||||
- (BOOL)listContainerView:(JXPagerListContainerView *)listContainerView canInitListAtIndex:(NSInteger)index;
|
||||
- (void)listContainerViewDidScroll:(UIScrollView *)scrollView;
|
||||
- (void)listContainerViewWillBeginDragging:(JXPagerListContainerView *)listContainerView;
|
||||
- (void)listContainerViewWDidEndScroll:(JXPagerListContainerView *)listContainerView;
|
||||
- (void)listContainerView:(JXPagerListContainerView *)listContainerView listDidAppearAtIndex:(NSInteger)index;
|
||||
|
||||
@end
|
||||
|
||||
@interface JXPagerListContainerView : UIView
|
||||
|
||||
@property (nonatomic, assign, readonly) JXPagerListContainerType containerType;
|
||||
@property (nonatomic, strong, readonly) UIScrollView *scrollView;
|
||||
@property (nonatomic, strong, readonly) NSDictionary <NSNumber *, id<JXPagerViewListViewDelegate>> *validListDict; //已经加载过的列表字典。key是index,value是对应的列表
|
||||
@property (nonatomic, strong) UIColor *listCellBackgroundColor; //默认:[UIColor whiteColor]
|
||||
/**
|
||||
滚动切换的时候,滚动距离超过一页的多少百分比,就触发列表的初始化。默认0.01(即列表显示了一点就触发加载)。范围0~1,开区间不包括0和1
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat initListPercent;
|
||||
///当使用Category嵌套Paging的时候,需要设置为YES,默认为NO;
|
||||
@property (nonatomic, assign, getter=isCategoryNestPagingEnabled) BOOL categoryNestPagingEnabled;
|
||||
@property (nonatomic, assign, readonly) NSInteger currentIndex;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
|
||||
- (instancetype)initWithType:(JXPagerListContainerType)type delegate:(id<JXPagerListContainerViewDelegate>)delegate NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
@interface JXPagerListContainerView (ListContainer)
|
||||
- (void)setDefaultSelectedIndex:(NSInteger)index;
|
||||
- (UIScrollView *)contentScrollView;
|
||||
- (void)reloadData;
|
||||
- (void)scrollingFromLeftIndex:(NSInteger)leftIndex toRightIndex:(NSInteger)rightIndex ratio:(CGFloat)ratio selectedIndex:(NSInteger)selectedIndex;
|
||||
- (void)didClickSelectedItemAtIndex:(NSInteger)index;
|
||||
@end
|
||||
|
||||
597
Pods/JXPagingView/Sources/JXPagerView/JXPagerListContainerView.m
generated
Normal file
597
Pods/JXPagingView/Sources/JXPagerView/JXPagerListContainerView.m
generated
Normal file
@@ -0,0 +1,597 @@
|
||||
//
|
||||
// JXPagerListContainerView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/9/12.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXPagerListContainerView.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface JXPagerListContainerScrollView: UIScrollView <UIGestureRecognizerDelegate>
|
||||
@property (nonatomic, assign, getter=isCategoryNestPagingEnabled) BOOL categoryNestPagingEnabled;
|
||||
@end
|
||||
@implementation JXPagerListContainerScrollView
|
||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
|
||||
if (self.isCategoryNestPagingEnabled) {
|
||||
if ([gestureRecognizer isMemberOfClass:NSClassFromString(@"UIScrollViewPanGestureRecognizer")]) {
|
||||
CGFloat velocityX = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:gestureRecognizer.view].x;
|
||||
//x大于0就是往右滑
|
||||
if (velocityX > 0) {
|
||||
if (self.contentOffset.x == 0) {
|
||||
return NO;
|
||||
}
|
||||
}else if (velocityX < 0) {
|
||||
//x小于0就是往左滑
|
||||
if (self.contentOffset.x + self.bounds.size.width == self.contentSize.width) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface JXPagerListContainerCollectionView: UICollectionView <UIGestureRecognizerDelegate>
|
||||
@property (nonatomic, assign, getter=isCategoryNestPagingEnabled) BOOL categoryNestPagingEnabled;
|
||||
@end
|
||||
@implementation JXPagerListContainerCollectionView
|
||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
|
||||
if (self.isCategoryNestPagingEnabled) {
|
||||
if ([gestureRecognizer isMemberOfClass:NSClassFromString(@"UIScrollViewPanGestureRecognizer")]) {
|
||||
CGFloat velocityX = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:gestureRecognizer.view].x;
|
||||
//x大于0就是往右滑
|
||||
if (velocityX > 0) {
|
||||
if (self.contentOffset.x == 0) {
|
||||
return NO;
|
||||
}
|
||||
}else if (velocityX < 0) {
|
||||
//x小于0就是往左滑
|
||||
if (self.contentOffset.x + self.bounds.size.width == self.contentSize.width) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface JXPagerListContainerViewController : UIViewController
|
||||
@property (copy) void(^viewWillAppearBlock)(void);
|
||||
@property (copy) void(^viewDidAppearBlock)(void);
|
||||
@property (copy) void(^viewWillDisappearBlock)(void);
|
||||
@property (copy) void(^viewDidDisappearBlock)(void);
|
||||
@end
|
||||
|
||||
@implementation JXPagerListContainerViewController
|
||||
- (void)dealloc
|
||||
{
|
||||
self.viewWillAppearBlock = nil;
|
||||
self.viewDidAppearBlock = nil;
|
||||
self.viewWillDisappearBlock = nil;
|
||||
self.viewDidDisappearBlock = nil;
|
||||
}
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
self.viewWillAppearBlock();
|
||||
}
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
self.viewDidAppearBlock();
|
||||
}
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
self.viewWillDisappearBlock();
|
||||
}
|
||||
- (void)viewDidDisappear:(BOOL)animated {
|
||||
[super viewDidDisappear:animated];
|
||||
self.viewDidDisappearBlock();
|
||||
}
|
||||
- (BOOL)shouldAutomaticallyForwardAppearanceMethods { return NO; }
|
||||
@end
|
||||
|
||||
@interface JXPagerListContainerView () <UIScrollViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource>
|
||||
@property (nonatomic, weak) id<JXPagerListContainerViewDelegate> delegate;
|
||||
@property (nonatomic, strong) UIScrollView *scrollView;
|
||||
@property (nonatomic, assign) NSInteger currentIndex;
|
||||
@property (nonatomic, strong) NSMutableDictionary <NSNumber *, id<JXPagerViewListViewDelegate>> *validListDict;
|
||||
@property (nonatomic, assign) NSInteger willAppearIndex;
|
||||
@property (nonatomic, assign) NSInteger willDisappearIndex;
|
||||
@property (nonatomic, strong) UICollectionView *collectionView;
|
||||
@property (nonatomic, strong) JXPagerListContainerViewController *containerVC;
|
||||
@end
|
||||
|
||||
@implementation JXPagerListContainerView
|
||||
|
||||
- (instancetype)initWithType:(JXPagerListContainerType)type delegate:(id<JXPagerListContainerViewDelegate>)delegate{
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self) {
|
||||
_containerType = type;
|
||||
_delegate = delegate;
|
||||
_validListDict = [NSMutableDictionary dictionary];
|
||||
_willAppearIndex = -1;
|
||||
_willDisappearIndex = -1;
|
||||
_initListPercent = 0.01;
|
||||
[self initializeViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initializeViews {
|
||||
_listCellBackgroundColor = [UIColor whiteColor];
|
||||
_containerVC = [[JXPagerListContainerViewController alloc] init];
|
||||
self.containerVC.view.backgroundColor = [UIColor clearColor];
|
||||
[self addSubview:self.containerVC.view];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.containerVC.viewWillAppearBlock = ^{
|
||||
[weakSelf listWillAppear:weakSelf.currentIndex];
|
||||
};
|
||||
self.containerVC.viewDidAppearBlock = ^{
|
||||
[weakSelf listDidAppear:weakSelf.currentIndex];
|
||||
};
|
||||
self.containerVC.viewWillDisappearBlock = ^{
|
||||
[weakSelf listWillDisappear:weakSelf.currentIndex];
|
||||
};
|
||||
self.containerVC.viewDidDisappearBlock = ^{
|
||||
[weakSelf listDidDisappear:weakSelf.currentIndex];
|
||||
};
|
||||
if (self.containerType == JXPagerListContainerType_ScrollView) {
|
||||
if (self.delegate &&
|
||||
[self.delegate respondsToSelector:@selector(scrollViewClassInlistContainerView:)] &&
|
||||
[[self.delegate scrollViewClassInlistContainerView:self] isKindOfClass:object_getClass([UIScrollView class])]) {
|
||||
_scrollView = (UIScrollView *)[[[self.delegate scrollViewClassInlistContainerView:self] alloc] init];
|
||||
}else {
|
||||
_scrollView = [[JXPagerListContainerScrollView alloc] init];
|
||||
}
|
||||
self.scrollView.backgroundColor = [UIColor clearColor];
|
||||
self.scrollView.delegate = self;
|
||||
self.scrollView.pagingEnabled = YES;
|
||||
self.scrollView.showsHorizontalScrollIndicator = NO;
|
||||
self.scrollView.showsVerticalScrollIndicator = NO;
|
||||
self.scrollView.scrollsToTop = NO;
|
||||
self.scrollView.bounces = NO;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
[self.containerVC.view addSubview:self.scrollView];
|
||||
}else {
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
layout.minimumLineSpacing = 0;
|
||||
layout.minimumInteritemSpacing = 0;
|
||||
if (self.delegate &&
|
||||
[self.delegate respondsToSelector:@selector(scrollViewClassInlistContainerView:)] &&
|
||||
[[self.delegate scrollViewClassInlistContainerView:self] isKindOfClass:object_getClass([UICollectionView class])]) {
|
||||
_collectionView = (UICollectionView *)[[[self.delegate scrollViewClassInlistContainerView:self] alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
}else {
|
||||
_collectionView = [[JXPagerListContainerCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
}
|
||||
self.collectionView.backgroundColor = [UIColor clearColor];
|
||||
self.collectionView.pagingEnabled = YES;
|
||||
self.collectionView.showsHorizontalScrollIndicator = NO;
|
||||
self.collectionView.showsVerticalScrollIndicator = NO;
|
||||
self.collectionView.scrollsToTop = NO;
|
||||
self.collectionView.bounces = NO;
|
||||
self.collectionView.dataSource = self;
|
||||
self.collectionView.delegate = self;
|
||||
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
|
||||
if (@available(iOS 10.0, *)) {
|
||||
self.collectionView.prefetchingEnabled = NO;
|
||||
}
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
[self.containerVC.view addSubview:self.collectionView];
|
||||
//让外部统一访问scrollView
|
||||
_scrollView = _collectionView;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willMoveToSuperview:(UIView *)newSuperview {
|
||||
[super willMoveToSuperview:newSuperview];
|
||||
|
||||
UIResponder *next = newSuperview;
|
||||
while (next != nil) {
|
||||
if ([next isKindOfClass:[UIViewController class]]) {
|
||||
[((UIViewController *)next) addChildViewController:self.containerVC];
|
||||
break;
|
||||
}
|
||||
next = next.nextResponder;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
self.containerVC.view.frame = self.bounds;
|
||||
if (self.containerType == JXPagerListContainerType_ScrollView) {
|
||||
if (CGRectEqualToRect(self.scrollView.frame, CGRectZero) || !CGSizeEqualToSize(self.scrollView.bounds.size, self.bounds.size)) {
|
||||
self.scrollView.frame = self.bounds;
|
||||
self.scrollView.contentSize = CGSizeMake(self.scrollView.bounds.size.width*[self.delegate numberOfListsInlistContainerView:self], self.scrollView.bounds.size.height);
|
||||
[_validListDict enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull index, id<JXPagerViewListViewDelegate> _Nonnull list, BOOL * _Nonnull stop) {
|
||||
[list listView].frame = CGRectMake(index.intValue*self.scrollView.bounds.size.width, 0, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height);
|
||||
}];
|
||||
self.scrollView.contentOffset = CGPointMake(self.currentIndex*self.scrollView.bounds.size.width, 0);
|
||||
}else {
|
||||
self.scrollView.frame = self.bounds;
|
||||
self.scrollView.contentSize = CGSizeMake(self.scrollView.bounds.size.width*[self.delegate numberOfListsInlistContainerView:self], self.scrollView.bounds.size.height);
|
||||
}
|
||||
}else {
|
||||
if (CGRectEqualToRect(self.collectionView.frame, CGRectZero) || !CGSizeEqualToSize(self.collectionView.bounds.size, self.bounds.size)) {
|
||||
self.collectionView.frame = self.bounds;
|
||||
[self.collectionView.collectionViewLayout invalidateLayout];
|
||||
[self.collectionView reloadData];
|
||||
[self.collectionView setContentOffset:CGPointMake(self.collectionView.bounds.size.width*self.currentIndex, 0) animated:NO];
|
||||
}else {
|
||||
self.collectionView.frame = self.bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)setinitListPercent:(CGFloat)initListPercent {
|
||||
_initListPercent = initListPercent;
|
||||
if (initListPercent <= 0 || initListPercent >= 1) {
|
||||
NSAssert(NO, @"initListPercent值范围为开区间(0,1),即不包括0和1");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCategoryNestPagingEnabled:(BOOL)categoryNestPagingEnabled {
|
||||
_categoryNestPagingEnabled = categoryNestPagingEnabled;
|
||||
if ([self.scrollView isKindOfClass:[JXPagerListContainerScrollView class]]) {
|
||||
((JXPagerListContainerScrollView *)self.scrollView).categoryNestPagingEnabled = categoryNestPagingEnabled;
|
||||
}else if ([self.scrollView isKindOfClass:[JXPagerListContainerCollectionView class]]) {
|
||||
((JXPagerListContainerCollectionView *)self.scrollView).categoryNestPagingEnabled = categoryNestPagingEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewDelegate, UICollectionViewDataSource
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return [self.delegate numberOfListsInlistContainerView:self];
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
|
||||
cell.contentView.backgroundColor = self.listCellBackgroundColor;
|
||||
for (UIView *subview in cell.contentView.subviews) {
|
||||
[subview removeFromSuperview];
|
||||
}
|
||||
id<JXPagerViewListViewDelegate> list = _validListDict[@(indexPath.item)];
|
||||
if (list != nil) {
|
||||
//fixme:如果list是UIViewController,如果这里的frame修改是`[list listView].frame = cell.bounds;`。那么就必须给list vc添加如下代码:
|
||||
//- (void)loadView {
|
||||
// self.view = [[UIView alloc] init];
|
||||
//}
|
||||
//所以,总感觉是把UIViewController当做普通view使用,导致了系统内部的bug。所以,缓兵之计就是用下面的方法,暂时解决问题。
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
[list listView].frame = cell.contentView.bounds;
|
||||
} else {
|
||||
[list listView].frame = cell.bounds;
|
||||
}
|
||||
[cell.contentView addSubview:[list listView]];
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return self.bounds.size;
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollViewDelegate
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerViewDidScroll:)]) {
|
||||
[self.delegate listContainerViewDidScroll:scrollView];
|
||||
}
|
||||
if (!scrollView.isDragging && !scrollView.isTracking && !scrollView.isDecelerating) {
|
||||
return;
|
||||
}
|
||||
CGFloat ratio = scrollView.contentOffset.x/scrollView.bounds.size.width;
|
||||
NSInteger maxCount = round(scrollView.contentSize.width/scrollView.bounds.size.width);
|
||||
NSInteger leftIndex = floorf(ratio);
|
||||
leftIndex = MAX(0, MIN(maxCount - 1, leftIndex));
|
||||
NSInteger rightIndex = leftIndex + 1;
|
||||
if (ratio < 0 || rightIndex >= maxCount) {
|
||||
[self listDidAppearOrDisappear:scrollView];
|
||||
return;
|
||||
}
|
||||
CGFloat remainderRatio = ratio - leftIndex;
|
||||
if (rightIndex == self.currentIndex) {
|
||||
//当前选中的在右边,用户正在从右边往左边滑动
|
||||
if (self.validListDict[@(leftIndex)] == nil && remainderRatio < (1 - self.initListPercent)) {
|
||||
[self initListIfNeededAtIndex:leftIndex];
|
||||
}else if (self.validListDict[@(leftIndex)] != nil) {
|
||||
if (self.willAppearIndex == -1) {
|
||||
self.willAppearIndex = leftIndex;
|
||||
[self listWillAppear:self.willAppearIndex];
|
||||
}
|
||||
}
|
||||
if (self.willDisappearIndex == -1) {
|
||||
self.willDisappearIndex = rightIndex;
|
||||
[self listWillDisappear:self.willDisappearIndex];
|
||||
}
|
||||
}else {
|
||||
//当前选中的在左边,用户正在从左边往右边滑动
|
||||
if (self.validListDict[@(rightIndex)] == nil && remainderRatio > self.initListPercent) {
|
||||
[self initListIfNeededAtIndex:rightIndex];
|
||||
}else if (self.validListDict[@(rightIndex)] != nil) {
|
||||
if (self.willAppearIndex == -1) {
|
||||
self.willAppearIndex = rightIndex;
|
||||
[self listWillAppear:self.willAppearIndex];
|
||||
}
|
||||
}
|
||||
if (self.willDisappearIndex == -1) {
|
||||
self.willDisappearIndex = leftIndex;
|
||||
[self listWillDisappear:self.willDisappearIndex];
|
||||
}
|
||||
}
|
||||
[self listDidAppearOrDisappear:scrollView];
|
||||
}
|
||||
|
||||
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
|
||||
if (self.willDisappearIndex != -1) {
|
||||
[self listWillAppear:self.willDisappearIndex];
|
||||
[self listWillDisappear:self.willAppearIndex];
|
||||
[self listDidAppear:self.willDisappearIndex];
|
||||
[self listDidDisappear:self.willAppearIndex];
|
||||
self.willDisappearIndex = -1;
|
||||
self.willAppearIndex = -1;
|
||||
}
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerViewWDidEndScroll:)]) {
|
||||
[self.delegate listContainerViewWDidEndScroll:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerViewWillBeginDragging:)]) {
|
||||
[self.delegate listContainerViewWillBeginDragging:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
|
||||
if (!decelerate) {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerViewWDidEndScroll:)]) {
|
||||
[self.delegate listContainerViewWDidEndScroll:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerViewWDidEndScroll:)]) {
|
||||
[self.delegate listContainerViewWDidEndScroll:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - JXCategoryViewListContainer
|
||||
|
||||
- (UIScrollView *)contentScrollView {
|
||||
return self.scrollView;
|
||||
}
|
||||
|
||||
- (void)setDefaultSelectedIndex:(NSInteger)index {
|
||||
self.currentIndex = index;
|
||||
}
|
||||
|
||||
- (void)scrollingFromLeftIndex:(NSInteger)leftIndex toRightIndex:(NSInteger)rightIndex ratio:(CGFloat)ratio selectedIndex:(NSInteger)selectedIndex {
|
||||
}
|
||||
|
||||
- (void)didClickSelectedItemAtIndex:(NSInteger)index {
|
||||
if (![self checkIndexValid:index]) {
|
||||
return;
|
||||
}
|
||||
self.willAppearIndex = -1;
|
||||
self.willDisappearIndex = -1;
|
||||
if (self.currentIndex != index) {
|
||||
[self listWillDisappear:self.currentIndex];
|
||||
[self listDidDisappear:self.currentIndex];
|
||||
[self listWillAppear:index];
|
||||
[self listDidAppear:index];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadData {
|
||||
for (id<JXPagerViewListViewDelegate> list in _validListDict.allValues) {
|
||||
[[list listView] removeFromSuperview];
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
[(UIViewController *)list removeFromParentViewController];
|
||||
}
|
||||
}
|
||||
[_validListDict removeAllObjects];
|
||||
|
||||
if (self.containerType == JXPagerListContainerType_ScrollView) {
|
||||
self.scrollView.contentSize = CGSizeMake(self.scrollView.bounds.size.width*[self.delegate numberOfListsInlistContainerView:self], self.scrollView.bounds.size.height);
|
||||
}else {
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
[self listWillAppear:self.currentIndex];
|
||||
[self listDidAppear:self.currentIndex];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)initListIfNeededAtIndex:(NSInteger)index {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerView:canInitListAtIndex:)]) {
|
||||
BOOL canInitList = [self.delegate listContainerView:self canInitListAtIndex:index];
|
||||
if (!canInitList) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
id<JXPagerViewListViewDelegate> list = _validListDict[@(index)];
|
||||
if (list != nil) {
|
||||
//列表已经创建好了
|
||||
return;
|
||||
}
|
||||
list = [self.delegate listContainerView:self initListForIndex:index];
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
[self.containerVC addChildViewController:(UIViewController *)list];
|
||||
}
|
||||
_validListDict[@(index)] = list;
|
||||
|
||||
switch (self.containerType) {
|
||||
case JXPagerListContainerType_ScrollView: {
|
||||
[list listView].frame = CGRectMake(index*self.scrollView.bounds.size.width, 0, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height);
|
||||
[self.scrollView addSubview:[list listView]];
|
||||
break;
|
||||
}
|
||||
case JXPagerListContainerType_CollectionView: {
|
||||
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
|
||||
if (cell != nil) {
|
||||
for (UIView *subview in cell.contentView.subviews) {
|
||||
[subview removeFromSuperview];
|
||||
}
|
||||
[list listView].frame = cell.contentView.bounds;
|
||||
[cell.contentView addSubview:[list listView]];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)listWillAppear:(NSInteger)index {
|
||||
if (![self checkIndexValid:index]) {
|
||||
return;
|
||||
}
|
||||
id<JXPagerViewListViewDelegate> list = _validListDict[@(index)];
|
||||
if (list != nil) {
|
||||
if (list && [list respondsToSelector:@selector(listWillAppear)]) {
|
||||
[list listWillAppear];
|
||||
}
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *listVC = (UIViewController *)list;
|
||||
[listVC beginAppearanceTransition:YES animated:NO];
|
||||
}
|
||||
}else {
|
||||
//当前列表未被创建(页面初始化或通过点击触发的listWillAppear)
|
||||
BOOL canInitList = YES;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerView:canInitListAtIndex:)]) {
|
||||
canInitList = [self.delegate listContainerView:self canInitListAtIndex:index];
|
||||
}
|
||||
if (canInitList) {
|
||||
id<JXPagerViewListViewDelegate> list = _validListDict[@(index)];
|
||||
if (list == nil) {
|
||||
list = [self.delegate listContainerView:self initListForIndex:index];
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
[self.containerVC addChildViewController:(UIViewController *)list];
|
||||
}
|
||||
_validListDict[@(index)] = list;
|
||||
}
|
||||
if (self.containerType == JXPagerListContainerType_ScrollView) {
|
||||
if ([list listView].superview == nil) {
|
||||
[list listView].frame = CGRectMake(index*self.scrollView.bounds.size.width, 0, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height);
|
||||
[self.scrollView addSubview:[list listView]];
|
||||
|
||||
if (list && [list respondsToSelector:@selector(listWillAppear)]) {
|
||||
[list listWillAppear];
|
||||
}
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *listVC = (UIViewController *)list;
|
||||
[listVC beginAppearanceTransition:YES animated:NO];
|
||||
}
|
||||
}
|
||||
}else {
|
||||
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
|
||||
for (UIView *subview in cell.contentView.subviews) {
|
||||
[subview removeFromSuperview];
|
||||
}
|
||||
[list listView].frame = cell.contentView.bounds;
|
||||
[cell.contentView addSubview:[list listView]];
|
||||
|
||||
if (list && [list respondsToSelector:@selector(listWillAppear)]) {
|
||||
[list listWillAppear];
|
||||
}
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *listVC = (UIViewController *)list;
|
||||
[listVC beginAppearanceTransition:YES animated:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)listDidAppear:(NSInteger)index {
|
||||
if (![self checkIndexValid:index]) {
|
||||
return;
|
||||
}
|
||||
self.currentIndex = index;
|
||||
id<JXPagerViewListViewDelegate> list = _validListDict[@(index)];
|
||||
if (list && [list respondsToSelector:@selector(listDidAppear)]) {
|
||||
[list listDidAppear];
|
||||
}
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *listVC = (UIViewController *)list;
|
||||
[listVC endAppearanceTransition];
|
||||
}
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerView:listDidAppearAtIndex:)]) {
|
||||
[self.delegate listContainerView:self listDidAppearAtIndex:index];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)listWillDisappear:(NSInteger)index {
|
||||
if (![self checkIndexValid:index]) {
|
||||
return;
|
||||
}
|
||||
id<JXPagerViewListViewDelegate> list = _validListDict[@(index)];
|
||||
if (list && [list respondsToSelector:@selector(listWillDisappear)]) {
|
||||
[list listWillDisappear];
|
||||
}
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *listVC = (UIViewController *)list;
|
||||
[listVC beginAppearanceTransition:NO animated:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)listDidDisappear:(NSInteger)index {
|
||||
if (![self checkIndexValid:index]) {
|
||||
return;
|
||||
}
|
||||
id<JXPagerViewListViewDelegate> list = _validListDict[@(index)];
|
||||
if (list && [list respondsToSelector:@selector(listDidDisappear)]) {
|
||||
[list listDidDisappear];
|
||||
}
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *listVC = (UIViewController *)list;
|
||||
[listVC endAppearanceTransition];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)checkIndexValid:(NSInteger)index {
|
||||
NSUInteger count = [self.delegate numberOfListsInlistContainerView:self];
|
||||
if (count <= 0 || index >= count) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)listDidAppearOrDisappear:(UIScrollView *)scrollView {
|
||||
CGFloat currentIndexPercent = scrollView.contentOffset.x/scrollView.bounds.size.width;
|
||||
if (self.willAppearIndex != -1 || self.willDisappearIndex != -1) {
|
||||
NSInteger disappearIndex = self.willDisappearIndex;
|
||||
NSInteger appearIndex = self.willAppearIndex;
|
||||
if (self.willAppearIndex > self.willDisappearIndex) {
|
||||
//将要出现的列表在右边
|
||||
if (currentIndexPercent >= self.willAppearIndex) {
|
||||
self.willDisappearIndex = -1;
|
||||
self.willAppearIndex = -1;
|
||||
[self listDidDisappear:disappearIndex];
|
||||
[self listDidAppear:appearIndex];
|
||||
}
|
||||
}else {
|
||||
//将要出现的列表在左边
|
||||
if (currentIndexPercent <= self.willAppearIndex) {
|
||||
self.willDisappearIndex = -1;
|
||||
self.willAppearIndex = -1;
|
||||
[self listDidDisappear:disappearIndex];
|
||||
[self listDidAppear:appearIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
14
Pods/JXPagingView/Sources/JXPagerView/JXPagerListRefreshView.h
generated
Normal file
14
Pods/JXPagingView/Sources/JXPagerView/JXPagerListRefreshView.h
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// JXPagingListRefreshView.h
|
||||
// JXPagingView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/28.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXPagerView.h"
|
||||
|
||||
@interface JXPagerListRefreshView : JXPagerView
|
||||
|
||||
@end
|
||||
109
Pods/JXPagingView/Sources/JXPagerView/JXPagerListRefreshView.m
generated
Normal file
109
Pods/JXPagingView/Sources/JXPagerView/JXPagerListRefreshView.m
generated
Normal file
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// JXPagerListRefreshView.m
|
||||
// JXPagerView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/28.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXPagerListRefreshView.h"
|
||||
|
||||
@interface JXPagerListRefreshView()
|
||||
@property (nonatomic, assign) CGFloat lastScrollingListViewContentOffsetY;
|
||||
@end
|
||||
|
||||
@implementation JXPagerListRefreshView
|
||||
|
||||
- (instancetype)initWithDelegate:(id<JXPagerViewDelegate>)delegate listContainerType:(JXPagerListContainerType)type {
|
||||
self = [super initWithDelegate:delegate listContainerType:type];
|
||||
if (self) {
|
||||
self.mainTableView.bounces = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)preferredProcessListViewDidScroll:(UIScrollView *)scrollView {
|
||||
BOOL shouldProcess = YES;
|
||||
if (self.currentScrollingListView.contentOffset.y > self.lastScrollingListViewContentOffsetY) {
|
||||
//往上滚动
|
||||
}else {
|
||||
//往下滚动
|
||||
if (self.mainTableView.contentOffset.y == 0) {
|
||||
shouldProcess = NO;
|
||||
}else {
|
||||
if (self.mainTableView.contentOffset.y < self.mainTableViewMaxContentOffsetY) {
|
||||
//mainTableView的header还没有消失,让listScrollView一直为0
|
||||
if (self.currentList && [self.currentList respondsToSelector:@selector(listScrollViewWillResetContentOffset)]) {
|
||||
[self.currentList listScrollViewWillResetContentOffset];
|
||||
}
|
||||
[self setListScrollViewToMinContentOffsetY:self.currentScrollingListView];
|
||||
if (self.automaticallyDisplayListVerticalScrollIndicator) {
|
||||
self.currentScrollingListView.showsVerticalScrollIndicator = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldProcess) {
|
||||
if (self.mainTableView.contentOffset.y < self.mainTableViewMaxContentOffsetY) {
|
||||
//处于下拉刷新的状态,scrollView.contentOffset.y为负数,就重置为0
|
||||
if (self.currentScrollingListView.contentOffset.y > [self minContentOffsetYInListScrollView:self.currentScrollingListView]) {
|
||||
//mainTableView的header还没有消失,让listScrollView一直为0
|
||||
if (self.currentList && [self.currentList respondsToSelector:@selector(listScrollViewWillResetContentOffset)]) {
|
||||
[self.currentList listScrollViewWillResetContentOffset];
|
||||
}
|
||||
[self setListScrollViewToMinContentOffsetY:self.currentScrollingListView];
|
||||
if (self.automaticallyDisplayListVerticalScrollIndicator) {
|
||||
self.currentScrollingListView.showsVerticalScrollIndicator = NO;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//mainTableView的header刚好消失,固定mainTableView的位置,显示listScrollView的滚动条
|
||||
self.mainTableView.contentOffset = CGPointMake(0, self.mainTableViewMaxContentOffsetY);
|
||||
if (self.automaticallyDisplayListVerticalScrollIndicator) {
|
||||
self.currentScrollingListView.showsVerticalScrollIndicator = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.lastScrollingListViewContentOffsetY = self.currentScrollingListView.contentOffset.y;
|
||||
}
|
||||
|
||||
- (void)preferredProcessMainTableViewDidScroll:(UIScrollView *)scrollView {
|
||||
if (self.pinSectionHeaderVerticalOffset != 0) {
|
||||
if (!(self.currentScrollingListView != nil && self.currentScrollingListView.contentOffset.y > [self minContentOffsetYInListScrollView:self.currentScrollingListView])) {
|
||||
//没有处于滚动某一个listView的状态
|
||||
if (scrollView.contentOffset.y <= 0) {
|
||||
self.mainTableView.bounces = NO;
|
||||
self.mainTableView.contentOffset = CGPointZero;
|
||||
return;
|
||||
}else {
|
||||
self.mainTableView.bounces = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
//正在下拉刷新时,不需要重置
|
||||
UIScrollView *listScrollView = [list listScrollView];
|
||||
if (listScrollView.contentOffset.y > 0) {
|
||||
if ([list respondsToSelector:@selector(listScrollViewWillResetContentOffset)]) {
|
||||
[list listScrollViewWillResetContentOffset];
|
||||
}
|
||||
[self setListScrollViewToMinContentOffsetY:listScrollView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scrollView.contentOffset.y > self.mainTableViewMaxContentOffsetY && self.currentScrollingListView.contentOffset.y == [self minContentOffsetYInListScrollView:self.currentScrollingListView]) {
|
||||
//当往上滚动mainTableView的headerView时,滚动到底时,修复listView往上小幅度滚动
|
||||
[self setMainTableViewToMaxContentOffsetY];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
19
Pods/JXPagingView/Sources/JXPagerView/JXPagerMainTableView.h
generated
Normal file
19
Pods/JXPagingView/Sources/JXPagerView/JXPagerMainTableView.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// JXPagingMainTableView.h
|
||||
// JXPagingView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/27.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol JXPagerMainTableViewGestureDelegate <NSObject>
|
||||
|
||||
- (BOOL)mainTableViewGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
|
||||
|
||||
@end
|
||||
|
||||
@interface JXPagerMainTableView : UITableView
|
||||
@property (nonatomic, weak) id<JXPagerMainTableViewGestureDelegate> gestureDelegate;
|
||||
@end
|
||||
25
Pods/JXPagingView/Sources/JXPagerView/JXPagerMainTableView.m
generated
Normal file
25
Pods/JXPagingView/Sources/JXPagerView/JXPagerMainTableView.m
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// JXPagerMainTableView.m
|
||||
// JXPagerView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/27.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXPagerMainTableView.h"
|
||||
|
||||
@interface JXPagerMainTableView ()<UIGestureRecognizerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation JXPagerMainTableView
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
|
||||
if (self.gestureDelegate && [self.gestureDelegate respondsToSelector:@selector(mainTableViewGestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]) {
|
||||
return [self.gestureDelegate mainTableViewGestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
|
||||
}else {
|
||||
return [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
91
Pods/JXPagingView/Sources/JXPagerView/JXPagerSmoothView.h
generated
Normal file
91
Pods/JXPagingView/Sources/JXPagerView/JXPagerSmoothView.h
generated
Normal file
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// JXPagerSmoothView.h
|
||||
// JXPagerViewExample-OC
|
||||
//
|
||||
// Created by jiaxin on 2019/11/15.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class JXPagerSmoothView;
|
||||
|
||||
@protocol JXPagerSmoothViewListViewDelegate <NSObject>
|
||||
/**
|
||||
返回listView。如果是vc包裹的就是vc.view;如果是自定义view包裹的,就是自定义view自己。
|
||||
*/
|
||||
- (UIView *)listView;
|
||||
/**
|
||||
返回JXPagerSmoothViewListViewDelegate内部持有的UIScrollView或UITableView或UICollectionView
|
||||
*/
|
||||
- (UIScrollView *)listScrollView;
|
||||
|
||||
@optional
|
||||
- (void)listDidAppear;
|
||||
- (void)listDidDisappear;
|
||||
|
||||
@end
|
||||
|
||||
@protocol JXPagerSmoothViewDataSource <NSObject>
|
||||
|
||||
/**
|
||||
返回页面header的高度
|
||||
*/
|
||||
- (CGFloat)heightForPagerHeaderInPagerView:(JXPagerSmoothView *)pagerView;
|
||||
|
||||
/**
|
||||
返回页面header视图
|
||||
*/
|
||||
- (UIView *)viewForPagerHeaderInPagerView:(JXPagerSmoothView *)pagerView;
|
||||
|
||||
/**
|
||||
返回悬浮视图的高度
|
||||
*/
|
||||
- (CGFloat)heightForPinHeaderInPagerView:(JXPagerSmoothView *)pagerView;
|
||||
|
||||
/**
|
||||
返回悬浮视图
|
||||
*/
|
||||
- (UIView *)viewForPinHeaderInPagerView:(JXPagerSmoothView *)pagerView;
|
||||
|
||||
/**
|
||||
返回列表的数量
|
||||
*/
|
||||
- (NSInteger)numberOfListsInPagerView:(JXPagerSmoothView *)pagerView;
|
||||
|
||||
/**
|
||||
根据index初始化一个对应列表实例,需要是遵从`JXPagerSmoothViewListViewDelegate`协议的对象。
|
||||
如果列表是用自定义UIView封装的,就让自定义UIView遵从`JXPagerSmoothViewListViewDelegate`协议,该方法返回自定义UIView即可。
|
||||
如果列表是用自定义UIViewController封装的,就让自定义UIViewController遵从`JXPagerSmoothViewListViewDelegate`协议,该方法返回自定义UIViewController即可。
|
||||
|
||||
@param pagerView pagerView description
|
||||
@param index index description
|
||||
@return 新生成的列表实例
|
||||
*/
|
||||
- (id<JXPagerSmoothViewListViewDelegate>)pagerView:(JXPagerSmoothView *)pagerView initListAtIndex:(NSInteger)index;
|
||||
|
||||
@end
|
||||
|
||||
@protocol JXPagerSmoothViewDelegate <NSObject>
|
||||
- (void)pagerSmoothViewDidScroll:(UIScrollView *)scrollView;
|
||||
@end
|
||||
|
||||
@interface JXPagerSmoothView : UIView
|
||||
|
||||
/**
|
||||
当前已经加载过的列表:key就是@(index)值,value是对应的列表。
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSDictionary <NSNumber *, id<JXPagerSmoothViewListViewDelegate>> *listDict;
|
||||
@property (nonatomic, strong, readonly) UICollectionView *listCollectionView;
|
||||
@property (nonatomic, assign) NSInteger defaultSelectedIndex;
|
||||
@property (nonatomic, weak) id<JXPagerSmoothViewDelegate> delegate;
|
||||
|
||||
- (instancetype)initWithDataSource:(id<JXPagerSmoothViewDataSource>)dataSource NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
|
||||
|
||||
- (void)reloadData;
|
||||
|
||||
@end
|
||||
|
||||
361
Pods/JXPagingView/Sources/JXPagerView/JXPagerSmoothView.m
generated
Normal file
361
Pods/JXPagingView/Sources/JXPagerView/JXPagerSmoothView.m
generated
Normal file
@@ -0,0 +1,361 @@
|
||||
//
|
||||
// JXPagerSmoothView.m
|
||||
// JXPagerViewExample-OC
|
||||
//
|
||||
// Created by jiaxin on 2019/11/15.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXPagerSmoothView.h"
|
||||
|
||||
static NSString *JXPagerSmoothViewCollectionViewCellIdentifier = @"cell";
|
||||
|
||||
@interface JXPagerSmoothCollectionView : UICollectionView <UIGestureRecognizerDelegate>
|
||||
@property (nonatomic, strong) UIView *pagerHeaderContainerView;
|
||||
@end
|
||||
@implementation JXPagerSmoothCollectionView
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
|
||||
CGPoint point = [touch locationInView:self.pagerHeaderContainerView];
|
||||
if (CGRectContainsPoint(self.pagerHeaderContainerView.bounds, point)) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface JXPagerSmoothView () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||
|
||||
@property (nonatomic, weak) id<JXPagerSmoothViewDataSource> dataSource;
|
||||
@property (nonatomic, strong) JXPagerSmoothCollectionView *listCollectionView;
|
||||
@property (nonatomic, strong) NSMutableDictionary <NSNumber *, id<JXPagerSmoothViewListViewDelegate>> *listDict;
|
||||
@property (nonatomic, strong) NSMutableDictionary <NSNumber *, UIView*> *listHeaderDict;
|
||||
@property (nonatomic, assign, getter=isSyncListContentOffsetEnabled) BOOL syncListContentOffsetEnabled;
|
||||
@property (nonatomic, strong) UIView *pagerHeaderContainerView;
|
||||
@property (nonatomic, assign) CGFloat currentPagerHeaderContainerViewY;
|
||||
@property (nonatomic, assign) NSInteger currentIndex;
|
||||
@property (nonatomic, strong) UIScrollView *currentListScrollView;
|
||||
@property (nonatomic, assign) CGFloat heightForPagerHeader;
|
||||
@property (nonatomic, assign) CGFloat heightForPinHeader;
|
||||
@property (nonatomic, assign) CGFloat heightForPagerHeaderContainerView;
|
||||
@property (nonatomic, assign) CGFloat currentListInitializeContentOffsetY;
|
||||
@property (nonatomic, strong) UIScrollView *singleScrollView;
|
||||
@end
|
||||
|
||||
@implementation JXPagerSmoothView
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
for (id<JXPagerSmoothViewListViewDelegate> list in self.listDict.allValues) {
|
||||
[[list listScrollView] removeObserver:self forKeyPath:@"contentOffset"];
|
||||
[[list listScrollView] removeObserver:self forKeyPath:@"contentSize"];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithDataSource:(id<JXPagerSmoothViewDataSource>)dataSource
|
||||
{
|
||||
self = [super initWithFrame:CGRectZero];
|
||||
if (self) {
|
||||
_dataSource = dataSource;
|
||||
_listDict = [NSMutableDictionary dictionary];
|
||||
_listHeaderDict = [NSMutableDictionary dictionary];
|
||||
[self initializeViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initializeViews {
|
||||
self.pagerHeaderContainerView = [[UIView alloc] init];
|
||||
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
layout.minimumLineSpacing = 0;
|
||||
layout.minimumInteritemSpacing = 0;
|
||||
_listCollectionView = [[JXPagerSmoothCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
self.listCollectionView.dataSource = self;
|
||||
self.listCollectionView.delegate = self;
|
||||
self.listCollectionView.pagingEnabled = YES;
|
||||
self.listCollectionView.bounces = NO;
|
||||
self.listCollectionView.showsHorizontalScrollIndicator = NO;
|
||||
self.listCollectionView.scrollsToTop = NO;
|
||||
[self.listCollectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:JXPagerSmoothViewCollectionViewCellIdentifier];
|
||||
if (@available(iOS 10.0, *)) {
|
||||
self.listCollectionView.prefetchingEnabled = NO;
|
||||
}
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.listCollectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
_listCollectionView.pagerHeaderContainerView = self.pagerHeaderContainerView;
|
||||
[self addSubview:self.listCollectionView];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
self.listCollectionView.frame = self.bounds;
|
||||
if (CGRectEqualToRect(self.pagerHeaderContainerView.frame, CGRectZero)) {
|
||||
[self reloadData];
|
||||
}
|
||||
if (self.singleScrollView != nil) {
|
||||
self.singleScrollView.frame = self.bounds;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadData {
|
||||
self.currentListScrollView = nil;
|
||||
self.currentIndex = self.defaultSelectedIndex;
|
||||
self.currentPagerHeaderContainerViewY = 0;
|
||||
self.syncListContentOffsetEnabled = NO;
|
||||
|
||||
[self.listHeaderDict removeAllObjects];
|
||||
for (id<JXPagerSmoothViewListViewDelegate> list in self.listDict.allValues) {
|
||||
[[list listScrollView] removeObserver:self forKeyPath:@"contentOffset"];
|
||||
[[list listScrollView] removeObserver:self forKeyPath:@"contentSize"];
|
||||
[[list listView] removeFromSuperview];
|
||||
}
|
||||
[_listDict removeAllObjects];
|
||||
|
||||
self.heightForPagerHeader = [self.dataSource heightForPagerHeaderInPagerView:self];
|
||||
self.heightForPinHeader = [self.dataSource heightForPinHeaderInPagerView:self];
|
||||
self.heightForPagerHeaderContainerView = self.heightForPagerHeader + self.heightForPinHeader;
|
||||
|
||||
UIView *pagerHeader = [self.dataSource viewForPagerHeaderInPagerView:self];
|
||||
UIView *pinHeader = [self.dataSource viewForPinHeaderInPagerView:self];
|
||||
[self.pagerHeaderContainerView addSubview:pagerHeader];
|
||||
[self.pagerHeaderContainerView addSubview:pinHeader];
|
||||
|
||||
self.pagerHeaderContainerView.frame = CGRectMake(0, 0, self.bounds.size.width, self.heightForPagerHeaderContainerView);
|
||||
pagerHeader.frame = CGRectMake(0, 0, self.bounds.size.width, self.heightForPagerHeader);
|
||||
pinHeader.frame = CGRectMake(0, self.heightForPagerHeader, self.bounds.size.width, self.heightForPinHeader);
|
||||
[self.listCollectionView setContentOffset:CGPointMake(self.listCollectionView.bounds.size.width*self.defaultSelectedIndex, 0) animated:NO];
|
||||
[self.listCollectionView reloadData];
|
||||
|
||||
if ([self.dataSource numberOfListsInPagerView:self] == 0) {
|
||||
self.singleScrollView = [[UIScrollView alloc] init];
|
||||
[self addSubview:self.singleScrollView];
|
||||
[self.singleScrollView addSubview:pagerHeader];
|
||||
self.singleScrollView.contentSize = CGSizeMake(self.bounds.size.width, self.heightForPagerHeader);
|
||||
}else if (self.singleScrollView != nil) {
|
||||
[self.singleScrollView removeFromSuperview];
|
||||
self.singleScrollView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewDataSource & UICollectionViewDelegateFlowLayout
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return self.bounds.size;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return [self.dataSource numberOfListsInPagerView:self];
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:JXPagerSmoothViewCollectionViewCellIdentifier forIndexPath:indexPath];
|
||||
id<JXPagerSmoothViewListViewDelegate> list = self.listDict[@(indexPath.item)];
|
||||
if (list == nil) {
|
||||
list = [self.dataSource pagerView:self initListAtIndex:indexPath.item];
|
||||
_listDict[@(indexPath.item)] = list;
|
||||
[[list listView] setNeedsLayout];
|
||||
[[list listView] layoutIfNeeded];
|
||||
UIScrollView *listScrollView = [list listScrollView];
|
||||
if ([listScrollView isKindOfClass:[UITableView class]]) {
|
||||
((UITableView *)listScrollView).estimatedRowHeight = 0;
|
||||
((UITableView *)listScrollView).estimatedSectionFooterHeight = 0;
|
||||
((UITableView *)listScrollView).estimatedSectionHeaderHeight = 0;
|
||||
}
|
||||
if (@available(iOS 11.0, *)) {
|
||||
listScrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
listScrollView.contentInset = UIEdgeInsetsMake(self.heightForPagerHeaderContainerView, 0, 0, 0);
|
||||
self.currentListInitializeContentOffsetY = -listScrollView.contentInset.top + MIN(-self.currentPagerHeaderContainerViewY, self.heightForPagerHeader);
|
||||
listScrollView.contentOffset = CGPointMake(0, self.currentListInitializeContentOffsetY);
|
||||
UIView *listHeader = [[UIView alloc] initWithFrame:CGRectMake(0, -self.heightForPagerHeaderContainerView, self.bounds.size.width, self.heightForPagerHeaderContainerView)];
|
||||
[listScrollView addSubview:listHeader];
|
||||
if (self.pagerHeaderContainerView.superview == nil) {
|
||||
[listHeader addSubview:self.pagerHeaderContainerView];
|
||||
}
|
||||
self.listHeaderDict[@(indexPath.item)] = listHeader;
|
||||
[listScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
|
||||
[listScrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
|
||||
}
|
||||
for (id<JXPagerSmoothViewListViewDelegate> listItem in self.listDict.allValues) {
|
||||
[listItem listScrollView].scrollsToTop = (listItem == list);
|
||||
}
|
||||
UIView *listView = [list listView];
|
||||
if (listView != nil && listView.superview != cell.contentView) {
|
||||
for (UIView *view in cell.contentView.subviews) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
listView.frame = cell.contentView.bounds;
|
||||
[cell.contentView addSubview:listView];
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[self listDidAppear:indexPath.item];
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[self listDidDisappear:indexPath.item];
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(pagerSmoothViewDidScroll:)]) {
|
||||
[self.delegate pagerSmoothViewDidScroll:scrollView];
|
||||
}
|
||||
CGFloat indexPercent = scrollView.contentOffset.x/scrollView.bounds.size.width;
|
||||
NSInteger index = floor(indexPercent);
|
||||
UIScrollView *listScrollView = [self.listDict[@(index)] listScrollView];
|
||||
if (indexPercent - index == 0 && index != self.currentIndex && !(scrollView.isDragging || scrollView.isDecelerating) && listScrollView.contentOffset.y <= -self.heightForPinHeader) {
|
||||
[self horizontalScrollDidEndAtIndex:index];
|
||||
}else {
|
||||
//左右滚动的时候,就把listHeaderContainerView添加到self,达到悬浮在顶部的效果
|
||||
if (self.pagerHeaderContainerView.superview != self) {
|
||||
self.pagerHeaderContainerView.frame = CGRectMake(0, self.currentPagerHeaderContainerViewY, self.pagerHeaderContainerView.bounds.size.width, self.pagerHeaderContainerView.bounds.size.height);
|
||||
[self addSubview:self.pagerHeaderContainerView];
|
||||
}
|
||||
}
|
||||
if (index != self.currentIndex) {
|
||||
self.currentIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
|
||||
if (!decelerate) {
|
||||
NSInteger index = scrollView.contentOffset.x/scrollView.bounds.size.width;
|
||||
[self horizontalScrollDidEndAtIndex:index];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
|
||||
NSInteger index = scrollView.contentOffset.x/scrollView.bounds.size.width;
|
||||
[self horizontalScrollDidEndAtIndex:index];
|
||||
}
|
||||
|
||||
#pragma mark - KVO
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
|
||||
if ([keyPath isEqualToString:@"contentOffset"]) {
|
||||
UIScrollView *scrollView = (UIScrollView *)object;
|
||||
if (scrollView != nil) {
|
||||
[self listDidScroll:scrollView];
|
||||
}
|
||||
}else if([keyPath isEqualToString:@"contentSize"]) {
|
||||
UIScrollView *scrollView = (UIScrollView *)object;
|
||||
if (scrollView != nil) {
|
||||
CGFloat minContentSizeHeight = self.bounds.size.height - self.heightForPinHeader;
|
||||
if (minContentSizeHeight > scrollView.contentSize.height) {
|
||||
scrollView.contentSize = CGSizeMake(scrollView.contentSize.width, minContentSizeHeight);
|
||||
//新的scrollView第一次加载的时候重置contentOffset
|
||||
if (_currentListScrollView != nil && scrollView != _currentListScrollView) {
|
||||
scrollView.contentOffset = CGPointMake(0, self.currentListInitializeContentOffsetY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Event
|
||||
|
||||
- (void)listDidScroll:(UIScrollView *)scrollView {
|
||||
if (self.listCollectionView.isDragging || self.listCollectionView.isDecelerating) {
|
||||
return;
|
||||
}
|
||||
NSInteger listIndex = [self listIndexForListScrollView:scrollView];
|
||||
if (listIndex != self.currentIndex) {
|
||||
return;
|
||||
}
|
||||
self.currentListScrollView = scrollView;
|
||||
CGFloat contentOffsetY = scrollView.contentOffset.y + self.heightForPagerHeaderContainerView;
|
||||
if (contentOffsetY < self.heightForPagerHeader) {
|
||||
self.syncListContentOffsetEnabled = YES;
|
||||
self.currentPagerHeaderContainerViewY = -contentOffsetY;
|
||||
for (id<JXPagerSmoothViewListViewDelegate> list in self.listDict.allValues) {
|
||||
if ([list listScrollView] != self.currentListScrollView) {
|
||||
[[list listScrollView] setContentOffset:scrollView.contentOffset animated:NO];
|
||||
}
|
||||
}
|
||||
UIView *listHeader = [self listHeaderForListScrollView:scrollView];
|
||||
if (self.pagerHeaderContainerView.superview != listHeader) {
|
||||
self.pagerHeaderContainerView.frame = CGRectMake(0, 0, self.pagerHeaderContainerView.bounds.size.width, self.pagerHeaderContainerView.bounds.size.height);
|
||||
[listHeader addSubview:self.pagerHeaderContainerView];
|
||||
}
|
||||
}else {
|
||||
if (self.pagerHeaderContainerView.superview != self) {
|
||||
self.pagerHeaderContainerView.frame = CGRectMake(0, -self.heightForPagerHeader, self.pagerHeaderContainerView.bounds.size.width, self.pagerHeaderContainerView.bounds.size.height);
|
||||
[self addSubview:self.pagerHeaderContainerView];
|
||||
}
|
||||
if (self.isSyncListContentOffsetEnabled) {
|
||||
self.syncListContentOffsetEnabled = NO;
|
||||
self.currentPagerHeaderContainerViewY = -self.heightForPagerHeader;
|
||||
for (id<JXPagerSmoothViewListViewDelegate> list in self.listDict.allValues) {
|
||||
if ([list listScrollView] != scrollView) {
|
||||
[[list listScrollView] setContentOffset:CGPointMake(0, -self.heightForPinHeader) animated:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (UIView *)listHeaderForListScrollView:(UIScrollView *)scrollView {
|
||||
for (NSNumber *index in self.listDict) {
|
||||
if ([self.listDict[index] listScrollView] == scrollView) {
|
||||
return self.listHeaderDict[index];
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSInteger)listIndexForListScrollView:(UIScrollView *)scrollView {
|
||||
for (NSNumber *index in self.listDict) {
|
||||
if ([self.listDict[index] listScrollView] == scrollView) {
|
||||
return [index integerValue];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)listDidAppear:(NSInteger)index {
|
||||
NSUInteger count = [self.dataSource numberOfListsInPagerView:self];
|
||||
if (count <= 0 || index >= count) {
|
||||
return;
|
||||
}
|
||||
|
||||
id<JXPagerSmoothViewListViewDelegate> list = self.listDict[@(index)];
|
||||
if (list && [list respondsToSelector:@selector(listDidAppear)]) {
|
||||
[list listDidAppear];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)listDidDisappear:(NSInteger)index {
|
||||
NSUInteger count = [self.dataSource numberOfListsInPagerView:self];
|
||||
if (count <= 0 || index >= count) {
|
||||
return;
|
||||
}
|
||||
id<JXPagerSmoothViewListViewDelegate> list = self.listDict[@(index)];
|
||||
if (list && [list respondsToSelector:@selector(listDidDisappear)]) {
|
||||
[list listDidDisappear];
|
||||
}
|
||||
}
|
||||
|
||||
/// 列表左右切换滚动结束之后,需要把pagerHeaderContainerView添加到当前index的列表上面
|
||||
- (void)horizontalScrollDidEndAtIndex:(NSInteger)index {
|
||||
self.currentIndex = index;
|
||||
UIView *listHeader = self.listHeaderDict[@(index)];
|
||||
UIScrollView *listScrollView = [self.listDict[@(index)] listScrollView];
|
||||
if (listHeader != nil && listScrollView.contentOffset.y <= -self.heightForPinHeader) {
|
||||
for (id<JXPagerSmoothViewListViewDelegate> listItem in self.listDict.allValues) {
|
||||
[listItem listScrollView].scrollsToTop = ([listItem listScrollView] == listScrollView);
|
||||
}
|
||||
self.pagerHeaderContainerView.frame = CGRectMake(0, 0, self.pagerHeaderContainerView.bounds.size.width, self.pagerHeaderContainerView.bounds.size.height);
|
||||
[listHeader addSubview:self.pagerHeaderContainerView];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
131
Pods/JXPagingView/Sources/JXPagerView/JXPagerView.h
generated
Normal file
131
Pods/JXPagingView/Sources/JXPagerView/JXPagerView.h
generated
Normal file
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// JXPagerView.h
|
||||
// JXPagerView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/27.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXPagerMainTableView.h"
|
||||
#import "JXPagerListContainerView.h"
|
||||
@class JXPagerView;
|
||||
|
||||
@protocol JXPagerViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
返回tableHeaderView的高度,因为内部需要比对判断,只能是整型数
|
||||
*/
|
||||
- (NSUInteger)tableHeaderViewHeightInPagerView:(JXPagerView *)pagerView;
|
||||
|
||||
/**
|
||||
返回tableHeaderView
|
||||
*/
|
||||
- (UIView *)tableHeaderViewInPagerView:(JXPagerView *)pagerView;
|
||||
|
||||
/**
|
||||
返回悬浮HeaderView的高度,因为内部需要比对判断,只能是整型数
|
||||
*/
|
||||
- (NSUInteger)heightForPinSectionHeaderInPagerView:(JXPagerView *)pagerView;
|
||||
|
||||
/**
|
||||
返回悬浮HeaderView。我用的是自己封装的JXCategoryView(Github:https://github.com/pujiaxin33/JXCategoryView),你也可以选择其他的三方库或者自己写
|
||||
*/
|
||||
- (UIView *)viewForPinSectionHeaderInPagerView:(JXPagerView *)pagerView;
|
||||
|
||||
/**
|
||||
返回列表的数量
|
||||
*/
|
||||
- (NSInteger)numberOfListsInPagerView:(JXPagerView *)pagerView;
|
||||
|
||||
/**
|
||||
根据index初始化一个对应列表实例,需要是遵从`JXPagerViewListViewDelegate`协议的对象。
|
||||
如果列表是用自定义UIView封装的,就让自定义UIView遵从`JXPagerViewListViewDelegate`协议,该方法返回自定义UIView即可。
|
||||
如果列表是用自定义UIViewController封装的,就让自定义UIViewController遵从`JXPagerViewListViewDelegate`协议,该方法返回自定义UIViewController即可。
|
||||
注意:一定要是新生成的实例!!!
|
||||
|
||||
@param pagerView pagerView description
|
||||
@param index index description
|
||||
@return 新生成的列表实例
|
||||
*/
|
||||
- (id<JXPagerViewListViewDelegate>)pagerView:(JXPagerView *)pagerView initListAtIndex:(NSInteger)index;
|
||||
|
||||
@optional
|
||||
|
||||
/// 返回对应index的列表唯一标识
|
||||
/// @param pagerView pagerView description
|
||||
/// @param index index description
|
||||
- (NSString *)pagerView:(JXPagerView *)pagerView listIdentifierAtIndex:(NSInteger)index;
|
||||
- (void)mainTableViewDidScroll:(UIScrollView *)scrollView __attribute__ ((deprecated));
|
||||
- (void)pagerView:(JXPagerView *)pagerView mainTableViewDidScroll:(UIScrollView *)scrollView;
|
||||
- (void)pagerView:(JXPagerView *)pagerView mainTableViewWillBeginDragging:(UIScrollView *)scrollView;
|
||||
- (void)pagerView:(JXPagerView *)pagerView mainTableViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
|
||||
- (void)pagerView:(JXPagerView *)pagerView mainTableViewDidEndDecelerating:(UIScrollView *)scrollView;
|
||||
- (void)pagerView:(JXPagerView *)pagerView mainTableViewDidEndScrollingAnimation:(UIScrollView *)scrollView;
|
||||
|
||||
/**
|
||||
返回自定义UIScrollView或UICollectionView的Class
|
||||
某些特殊情况需要自己处理列表容器内UIScrollView内部逻辑。比如项目用了FDFullscreenPopGesture,需要处理手势相关代理。
|
||||
|
||||
@param pagerView JXPagerView
|
||||
@return 自定义UIScrollView实例
|
||||
*/
|
||||
- (Class)scrollViewClassInlistContainerViewInPagerView:(JXPagerView *)pagerView;
|
||||
|
||||
@end
|
||||
|
||||
@interface JXPagerView : UIView
|
||||
/**
|
||||
需要和self.categoryView.defaultSelectedIndex保持一致
|
||||
*/
|
||||
@property (nonatomic, assign) NSInteger defaultSelectedIndex;
|
||||
@property (nonatomic, strong, readonly) JXPagerMainTableView *mainTableView;
|
||||
@property (nonatomic, strong, readonly) JXPagerListContainerView *listContainerView;
|
||||
/**
|
||||
当前已经加载过可用的列表字典,key就是index值,value是对应的列表。
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) NSDictionary <NSNumber *, id<JXPagerViewListViewDelegate>> *validListDict;
|
||||
/**
|
||||
顶部固定sectionHeader的垂直偏移量。数值越大越往下沉。
|
||||
*/
|
||||
@property (nonatomic, assign) NSInteger pinSectionHeaderVerticalOffset;
|
||||
/**
|
||||
是否允许列表左右滑动。默认:YES
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL isListHorizontalScrollEnabled;
|
||||
/**
|
||||
是否允许当前列表自动显示或隐藏列表是垂直滚动指示器。YES:悬浮的headerView滚动到顶部开始滚动列表时,就会显示,反之隐藏。NO:内部不会处理列表的垂直滚动指示器。默认为:YES。
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL automaticallyDisplayListVerticalScrollIndicator;
|
||||
/**
|
||||
当allowsCacheList为true时,请务必实现代理方法`- (NSString *)pagerView:(JXPagerView *)pagerView listIdentifierAtIndex:(NSInteger)index`
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL allowsCacheList;
|
||||
|
||||
- (instancetype)initWithDelegate:(id<JXPagerViewDelegate>)delegate;
|
||||
- (instancetype)initWithDelegate:(id<JXPagerViewDelegate>)delegate listContainerType:(JXPagerListContainerType)type NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
|
||||
- (void)reloadData;
|
||||
- (void)resizeTableHeaderViewHeightWithAnimatable:(BOOL)animatable duration:(NSTimeInterval)duration curve:(UIViewAnimationCurve)curve;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
暴露给子类使用,请勿直接使用相关属性和方法!
|
||||
*/
|
||||
@interface JXPagerView (UISubclassingGet)
|
||||
@property (nonatomic, strong, readonly) UIScrollView *currentScrollingListView;
|
||||
@property (nonatomic, strong, readonly) id<JXPagerViewListViewDelegate> currentList;
|
||||
@property (nonatomic, assign, readonly) CGFloat mainTableViewMaxContentOffsetY;
|
||||
@end
|
||||
|
||||
@interface JXPagerView (UISubclassingHooks)
|
||||
- (void)preferredProcessListViewDidScroll:(UIScrollView *)scrollView;
|
||||
- (void)preferredProcessMainTableViewDidScroll:(UIScrollView *)scrollView;
|
||||
- (void)setMainTableViewToMaxContentOffsetY;
|
||||
- (void)setListScrollViewToMinContentOffsetY:(UIScrollView *)scrollView;
|
||||
- (CGFloat)minContentOffsetYInListScrollView:(UIScrollView *)scrollView;
|
||||
@end
|
||||
|
||||
409
Pods/JXPagingView/Sources/JXPagerView/JXPagerView.m
generated
Normal file
409
Pods/JXPagingView/Sources/JXPagerView/JXPagerView.m
generated
Normal file
@@ -0,0 +1,409 @@
|
||||
//
|
||||
// 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
|
||||
14
Pods/JXPagingView/Sources/PrivacyInfo.xcprivacy
generated
Normal file
14
Pods/JXPagingView/Sources/PrivacyInfo.xcprivacy
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user