提交
This commit is contained in:
21
Pods/JXCategoryView/LICENSE
generated
Normal file
21
Pods/JXCategoryView/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.
|
||||
296
Pods/JXCategoryView/README.md
generated
Normal file
296
Pods/JXCategoryView/README.md
generated
Normal file
@@ -0,0 +1,296 @@
|
||||
<div align=center><img src="Example/Example/Images/JXCategoryView.png" width="405" height="63" /></div>
|
||||
|
||||
[](#)
|
||||
[](#)
|
||||
[](https://cocoapods.org/pods/JXCategoryView)
|
||||
[](#)
|
||||
|
||||
A powerful and easy to use category view (segmentedcontrol, segmentview, pagingview, pagerview, pagecontrol) (腾讯新闻、今日头条、QQ 音乐、网易云音乐、京东、爱奇艺、腾讯视频、淘宝、天猫、简书、微博等所有主流 APP 分类切换滚动视图)
|
||||
|
||||
与其他的同类三方库对比的优点:
|
||||
- 使用协议封装指示器逻辑,可以随心所欲地自定义指示器效果;
|
||||
- 提供更加全面丰富、高度自定义的效果;
|
||||
- 使用子类化管理 cell 样式,逻辑更清晰,扩展更简单;
|
||||
- 高度封装列表容器,使用便捷,完美支持列表的生命周期调用;
|
||||
|
||||
## Swift版本
|
||||
|
||||
如果你在找 Swift 版本,请点击查看 [JXSegmentedView](https://github.com/pujiaxin33/JXSegmentedView)。
|
||||
|
||||
## 效果预览
|
||||
|
||||
### 指示器效果预览
|
||||
|
||||
说明 | Gif |
|
||||
----|------|
|
||||
LineView | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/LineView.gif" width="343" height="80"> |
|
||||
LineView延长 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/JDLineStyle.gif" width="343" height="80"> |
|
||||
LineView延长+偏移 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/IQIYILineStyle.gif" width="343" height="80"> |
|
||||
LineView🌈彩虹风格 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/RainbowLineView.gif" width="343" height="80"> |
|
||||
DotLineView点线效果 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/IndicatorCustomizeGuide.gif" width="334" height="88"> |
|
||||
BallView QQ黏性红点 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/QQBall.gif" width="343" height="84"> |
|
||||
TriangleView 三角形底部 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TriangleBottom.gif" width="343" height="80"> |
|
||||
TriangleView 三角形顶部 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TriangleTop.gif" width="343" height="80"> |
|
||||
BackgroundView椭圆形 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/BackgroundEllipseLayer.gif" width="343" height="80"> |
|
||||
BackgroundView椭圆形+阴影 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/BackgroundViewShadow.gif" width="343" height="80"> |
|
||||
BackgroundView长方形 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Rectangle.gif" width="343" height="80"> |
|
||||
BackgroundView遮罩有背景 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleMask.gif" width="343" height="80"> |
|
||||
BackgroundView遮罩无背景 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleMaskNoBackgroundView.gif" width="343" height="80"> |
|
||||
BackgroundView渐变色 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/GradientBGIndicatorView.gif" width="350" height="80"> |
|
||||
ImageView底部(小船) | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/IndicatorImageView.gif" width="343" height="137"> |
|
||||
ImageView背景(最佳男歌手) | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/BackgroundImageView.gif" width="343" height="80"> |
|
||||
ImageView滚动效果(足球) | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Football.gif" width="343" height="135"> |
|
||||
混合使用 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Mixed.gif" width="343" height="80"> |
|
||||
|
||||
以下均支持上下位置切换:
|
||||
`JXCategoryIndicatorLineView`、`JXCategoryIndicatorImageView`、`JXCategoryIndicatorBallView`、`JXCategoryIndicatorTriangleView`
|
||||
|
||||
### Cell样式效果预览
|
||||
|
||||
说明 | Gif |
|
||||
----|------|
|
||||
颜色渐变 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleColorGradient.gif" width="343" height="80"> |
|
||||
大小缩放 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleZoom.gif" width="350" height="80"> |
|
||||
大小缩放+底部锚点 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleLabelAnchorBottom.gif" width="350" height="80"> |
|
||||
大小缩放+顶部锚点 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleLabelAnchorTop.gif" width="350" height="80"> |
|
||||
大小缩放+字体粗细 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleZoomStrokeWidth.gif" width="350" height="80"> |
|
||||
大小缩放+点击动画 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleZoomSelectedAnimation.gif" width="350" height="80"> |
|
||||
大小缩放+cell宽度缩放 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleZoomCellWidth.gif" width="350" height="80"> |
|
||||
TitleImage_Top | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleImageTop.gif" width="343" height="80"> |
|
||||
TitleImage_Left | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleImageLeft.gif" width="343" height="80"> |
|
||||
TitleImage_Bottom | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleImageBottom.gif" width="343" height="80"> |
|
||||
TitleImage_Right | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleImageRight.gif" width="343" height="80"> |
|
||||
cell图文混用 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CellMixed.gif" width="343" height="90"> |
|
||||
Image | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CellImage.gif" width="343" height="80"> |
|
||||
数字 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Number.gif" width="343" height="80"> |
|
||||
红点 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CellRedDot.gif" width="343" height="80"> |
|
||||
多行文本 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/MultiLineText.gif" width="350" height="80"> |
|
||||
多行富文本 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/AttributeView.gif" width="343" height="80"> |
|
||||
Cell背景色渐变 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CellBackgroundColorGradient.gif" width="343" height="80"> |
|
||||
分割线 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/SeparatorLine.gif" width="343" height="80"> |
|
||||
|
||||
### 特殊效果预览
|
||||
|
||||
说明 | Gif |
|
||||
----|------|
|
||||
数据源过少<br/> averageCellSpacingEnabled默认为YES | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/averageCellSpacingEnabledYES.gif" width="343" height="80"> |
|
||||
数据源过少<br/> averageCellSpacingEnabled为NO | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/averageCellSpacingEnabledNO.gif" width="343" height="80"> |
|
||||
SegmentedControl<br/>参考[`SegmentedControlViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/SegmentedControl/SegmentedControlViewController.m)类 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/SegmentedControl.gif" width="343" height="80"> |
|
||||
导航栏使用<br/>参考[`NaviSegmentedControlViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/SegmentedControl/NaviSegmentedControlViewController.m)类 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/SegmentedControlNavi.gif" width="343" height="80"> |
|
||||
嵌套使用<br/>参考[`NestViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/Nest/NestViewController.m)类 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/Nest.gif" width="343" height="272"> |
|
||||
个人主页(上下左右滚动、header悬浮)<br/>参考[`PagingViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/PagingView/Example/PagingViewController.m)类<br/> 更多样式请点击查看[JXPagingView库](https://github.com/pujiaxin33/JXPagingView) | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/UserProfile.gif" width="343" height="562"> |
|
||||
垂直列表滚动<br/>参考[`VerticalListViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/VerticalListView/VerticalListViewController.m)类<br/> 高仿腾讯视频<br/>支持UITableView,参考[`VerticalListTableViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/VerticalListView/VerticalListTableViewController.m)<br/>(背景色异常是录屏软件bug) | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/VerticalList.gif" width="343" height="607"> |
|
||||
| 垂直缩放(仿网易圈圈、脉脉首页)<br/>参考[`ScrollZoomViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/ScrollZoom/ScrollZoomViewController.m)类 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/VerticalZoomTitle.gif" width="350" height="306"> |
|
||||
数据源刷新&列表数据加载<br/>参考[`LoadDataListContainerViewController`](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/LoadData/LoadDataListContainerViewController.m)类 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/LoadData.gif" width="343" height="619"> |
|
||||
上下滚动隐藏导航栏 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/ScrollUp.gif" width="336" height="354"> |
|
||||
京东首页-滚动渐变变小 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/jingdo_scroll_small.gif" width="305" height="599"> |
|
||||
|
||||
### 自定义效果预览
|
||||
|
||||
收录来自其他使用者的自定义示例,这些自定义类只在 Demo 项目里面,Pod 库并没有这些文件。所以,如果你需要使用这些自定义效果,请通过文件导入的方式。
|
||||
|
||||
目的:
|
||||
- 参考学习如何自定义;
|
||||
- 直接修改自定义示例类以快速实现自己的需求。
|
||||
|
||||
欢迎提 PullRequest 进行收录你的自定义效果。
|
||||
|
||||
| 说明 | Gif |
|
||||
| ----|------|
|
||||
| Spring动画指示器 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/SpringIndicator.gif" width="336" height="70"> |
|
||||
| 富文本数量cell | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/CountCell.gif" width="336" height="70"> |
|
||||
| 左右对齐指示器 | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/AlignmentIndicator.gif" width="336" height="70"> |
|
||||
| 秒杀时间线cell | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/timeline.gif" width="336" height="70"> |
|
||||
| 京东商品排序cell | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/JDProductSort.gif" width="336" height="70"> |
|
||||
| title背景块cell | <img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/TitleBackgroundExample.gif" width="336" height="70"> |
|
||||
|
||||
## 要求
|
||||
|
||||
- iOS 9.0+
|
||||
- Xcode 9+
|
||||
- Objective-C
|
||||
|
||||
## 安装
|
||||
|
||||
### 手动
|
||||
|
||||
Clone 代码,把 Sources 文件夹拖入项目,`#import "JXCategoryView.h"` 就可以使用了。
|
||||
|
||||
### CocoaPods
|
||||
|
||||
```ruby
|
||||
target '<Your Target Name>' do
|
||||
pod 'JXCategoryView'
|
||||
end
|
||||
```
|
||||
先执行 `pod repo update`,再执行 `pod install`。
|
||||
|
||||
## 结构图
|
||||
|
||||
<img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/JXCategoryViewStructure.png" width="933" height="482">
|
||||
|
||||
## 使用
|
||||
|
||||
### JXCategoryTitleView 使用示例
|
||||
|
||||
1. 初始化 `JXCategoryTitleView`:
|
||||
```Objective-C
|
||||
self.categoryView = [[JXCategoryTitleView alloc] initWithFrame:CGRectMake(0, 0, WindowsSize.width, 50)];
|
||||
self.categoryView.delegate = self;
|
||||
[self.view addSubview:self.categoryView];
|
||||
```
|
||||
|
||||
2. 配置 `JXCategoryTitleView` 的属性:
|
||||
```Objective-C
|
||||
self.categoryView.titles = @[@"螃蟹", @"麻辣小龙虾", @"苹果"...];
|
||||
self.categoryView.titleColorGradientEnabled = YES;
|
||||
```
|
||||
|
||||
3. 添加指示器:
|
||||
```Objective-C
|
||||
JXCategoryIndicatorLineView *lineView = [[JXCategoryIndicatorLineView alloc] init];
|
||||
lineView.indicatorColor = [UIColor redColor];
|
||||
lineView.indicatorWidth = JXCategoryViewAutomaticDimension;
|
||||
self.categoryView.indicators = @[lineView];
|
||||
```
|
||||
|
||||
4. 实现 `JXCategoryViewDelegate` 代理(可选)
|
||||
```Objective-C
|
||||
// 点击选中或者滚动选中都会调用该方法。适用于只关心选中事件,不关心具体是点击还是滚动选中的。
|
||||
- (void)categoryView:(JXCategoryBaseView *)categoryView didSelectedItemAtIndex:(NSInteger)index;
|
||||
|
||||
// 点击选中的情况才会调用该方法
|
||||
- (void)categoryView:(JXCategoryBaseView *)categoryView didClickSelectedItemAtIndex:(NSInteger)index;
|
||||
|
||||
// 滚动选中的情况才会调用该方法
|
||||
- (void)categoryView:(JXCategoryBaseView *)categoryView didScrollSelectedItemAtIndex:(NSInteger)index;
|
||||
|
||||
// 正在滚动中的回调
|
||||
- (void)categoryView:(JXCategoryBaseView *)categoryView scrollingFromLeftIndex:(NSInteger)leftIndex toRightIndex:(NSInteger)rightIndex ratio:(CGFloat)ratio;
|
||||
```
|
||||
|
||||
### 列表容器使用示例
|
||||
|
||||
#### `JXCategoryListContainerView` 封装类使用示例
|
||||
|
||||
`JXCategoryListContainerView` 是对列表视图高度封装的类,具有以下优点:
|
||||
|
||||
- 相对于直接使用 `UIScrollView` 自定义,封装度高、代码集中、使用简单;
|
||||
- 列表懒加载:当显示某个列表的时候,才进行列表初始化。而不是一次性加载全部列表,性能更优;
|
||||
- 支持列表的 `willAppear`、`didAppear`、`willDisappear`、`didDisappear` 生命周期方法调用;
|
||||
|
||||
1. 初始化 `JXCategoryListContainerView` 并关联到 `categoryView`:
|
||||
```Objective-C
|
||||
self.listContainerView = [[JXCategoryListContainerView alloc] initWithType:JXCategoryListContainerType_ScrollView delegate:self];
|
||||
[self.view addSubview:self.listContainerView];
|
||||
// 关联到 categoryView
|
||||
self.categoryView.listContainer = self.listContainerView;
|
||||
```
|
||||
|
||||
2. 实现 `JXCategoryListContainerViewDelegate` 代理方法:
|
||||
```Objective-C
|
||||
// 返回列表的数量
|
||||
- (NSInteger)numberOfListsInlistContainerView:(JXCategoryListContainerView *)listContainerView {
|
||||
return self.titles.count;
|
||||
}
|
||||
// 根据下标 index 返回对应遵守并实现 `JXCategoryListContentViewDelegate` 协议的列表实例
|
||||
- (id<JXCategoryListContentViewDelegate>)listContainerView:(JXCategoryListContainerView *)listContainerView initListForIndex:(NSInteger)index {
|
||||
return [[ListViewController alloc] init];
|
||||
}
|
||||
```
|
||||
|
||||
3. 列表实现 `JXCategoryListContentViewDelegate` 代理方法
|
||||
不管列表是 `UIView` 还是 `UIViewController` 都可以,提高使用灵活性,更便于现有的业务接入。
|
||||
```Objective-C
|
||||
// 返回列表视图
|
||||
// 如果列表是 VC,就返回 VC.view
|
||||
// 如果列表是 View,就返回 View 自己
|
||||
- (UIView *)listView {
|
||||
return self.view;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
具体点击 [LoadDataListContainerViewController](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/LoadData/LoadDataListContainerViewController.m) 查看源代码了解
|
||||
|
||||
#### 直接使用 UIScrollView 自定义
|
||||
|
||||
因为代码量较多且分散,所有不推荐使用该方法。要正确使用需要注意的地方比较多,尤其对于刚接触 iOS 的同学来说不太友好。
|
||||
|
||||
不直接贴代码了,具体点击 [LoadDataListCustomViewController](https://github.com/pujiaxin33/JXCategoryView/tree/master/Example/Example/Examples/LoadData/LoadDataListCustomViewController.m) 查看源代码了解。
|
||||
|
||||
|
||||
## 常见问题和答案
|
||||
|
||||
❗️❗️❗️这里面包含了许多常见问题和答案,使用之前请务必浏览此文档,或者遇到问题先看此文档❗️❗️❗️
|
||||
|
||||
[常见问题和答案总文档](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md)
|
||||
|
||||
- [个人主页效果更丰富的示例:JXPagingView](https://github.com/pujiaxin33/JXPagingView)
|
||||
- [侧滑手势处理](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BE%A7%E6%BB%91%E6%89%8B%E5%8A%BF%E5%A4%84%E7%90%86.md)
|
||||
- [列表的生命周期方法处理](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E5%88%97%E8%A1%A8%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E6%96%B9%E6%B3%95%E5%A4%84%E7%90%86.md)
|
||||
- [`JXCategoryListContainerType`的`scrollView`和`collectionView`对比](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#jxcategorylistcontainertype%E7%9A%84scrollview%E5%92%8Ccollectionview%E5%AF%B9%E6%AF%94)
|
||||
- [cell左滑删除](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#cell%E5%B7%A6%E6%BB%91%E5%88%A0%E9%99%A4)
|
||||
- [`FDFullscreenPopGesture`等全屏手势处理](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E5%85%A8%E5%B1%8F%E6%89%8B%E5%8A%BF%E5%A4%84%E7%90%86.md)
|
||||
- [JXCategoryView数据源刷新](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#jxcategoryview%E6%95%B0%E6%8D%AE%E6%BA%90%E5%88%B7%E6%96%B0)
|
||||
- [`reloadDataWithoutListContainer`方法使用说明](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#reloaddatawithoutlistcontainer%E6%96%B9%E6%B3%95%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E)
|
||||
- [listContainer或contentScrollView关联说明](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#listcontainer%E6%88%96contentscrollview%E5%85%B3%E8%81%94%E8%AF%B4%E6%98%8E)
|
||||
- [点击切换列表的动画控制](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E7%82%B9%E5%87%BB%E5%88%87%E6%8D%A2%E5%88%97%E8%A1%A8%E7%9A%84%E5%8A%A8%E7%94%BB%E6%8E%A7%E5%88%B6)
|
||||
- [列表cell点击跳转示例](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%88%97%E8%A1%A8cell%E7%82%B9%E5%87%BB%E8%B7%B3%E8%BD%AC%E7%A4%BA%E4%BE%8B)
|
||||
- [列表调用`presentViewController`方法](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%88%97%E8%A1%A8%E8%B0%83%E7%94%A8presentviewcontroller%E6%96%B9%E6%B3%95)
|
||||
- [代码选中指定index](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E4%BB%A3%E7%A0%81%E9%80%89%E4%B8%AD%E6%8C%87%E5%AE%9Aindex)
|
||||
- [JXCategoryView.collectionView高度取整说明](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#jxcategoryviewcollectionview%E9%AB%98%E5%BA%A6%E5%8F%96%E6%95%B4%E8%AF%B4%E6%98%8E)
|
||||
- [对父VC的automaticallyAdjustsScrollViewInsets属性设置为NO](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%AF%B9%E7%88%B6vc%E7%9A%84automaticallyadjustsscrollviewinsets%E5%B1%9E%E6%80%A7%E8%AE%BE%E7%BD%AE%E4%B8%BAno)
|
||||
- [`JXCategoryListContainerView`内部使用`UIViewController`当做列表容器使用说明](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#jxcategorylistcontainerview%E5%86%85%E9%83%A8%E4%BD%BF%E7%94%A8uiviewcontroller%E5%BD%93%E5%81%9A%E5%88%97%E8%A1%A8%E5%AE%B9%E5%99%A8%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E)
|
||||
- [使用多行文本](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E4%BD%BF%E7%94%A8%E5%A4%9A%E8%A1%8C%E6%96%87%E6%9C%AC)
|
||||
- [列表容器禁止左右滑动](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%88%97%E8%A1%A8%E5%AE%B9%E5%99%A8%E7%A6%81%E6%AD%A2%E5%B7%A6%E5%8F%B3%E6%BB%91%E5%8A%A8)
|
||||
- [单个cell刷新 ](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E5%8D%95%E4%B8%AAcell%E5%88%B7%E6%96%B0)
|
||||
- [点击item时指示器和列表滚动时效果一致](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E7%82%B9%E5%87%BBitem%E6%97%B6%E6%8C%87%E7%A4%BA%E5%99%A8%E5%92%8C%E5%88%97%E8%A1%A8%E6%BB%9A%E5%8A%A8%E6%97%B6%E6%95%88%E6%9E%9C%E4%B8%80%E8%87%B4)
|
||||
- [自定义建议](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BB%BA%E8%AE%AE)
|
||||
|
||||
## 指示器样式自定义
|
||||
|
||||
- 需要继承 `JXCategoryIndicatorProtocol` 协议,点击参看 [JXCategoryIndicatorProtocol](https://github.com/pujiaxin33/JXCategoryView/blob/master/Sources/Common/JXCategoryIndicatorProtocol.h);
|
||||
- 提供了继承 `JXCategoryIndicatorProtocol` 协议的基类 `JXCategoryIndicatorComponentView`,里面提供了许多基础属性。点击参看 [JXCategoryIndicatorComponentView](https://github.com/pujiaxin33/JXCategoryView/blob/master/Sources/Indicator/IndicatorViews/JXCategoryIndicatorComponentView.m);
|
||||
- 自定义指示器,请参考已实现的指示器视图,多尝试、多思考,再有问题请提 Issue 或加入反馈 QQ 群。
|
||||
|
||||
## Cell 自定义
|
||||
|
||||
- 任何子类化需求,view、cell、cellModel 三个都要子类化,即使某个子类 cell 什么事情都不做。用于维护继承链,以免以后子类化都不知道要继承谁了;
|
||||
- 如果你想完全自定义 cell 里面的内容,那就继承 `JXCategoryIndicatorView`、`JXCategoryIndicatorCell、`JXCategoryIndicatorCellModel`,就像`JXCategoryTitleView`、`JXCategoryTitleCell、`JXCategoryTitleCellModel`那样去做;
|
||||
- 如果你只是在父类进行一些微调,那就继承目标 view、cell、cellModel,对 cell 原有控件微调、或者加入新的控件皆可。就像 `JXCategoryTitleImageView` 系列、`JXCategoryTitleAttributeView` 系列那样去做;
|
||||
- Cell 自定义,请参考已实现的 cell 样式,多尝试、多思考,再有问题请提 Issue 或加入反馈 QQ 群
|
||||
|
||||
## 常用属性说明
|
||||
|
||||
[常用属性说明文档地址](https://github.com/pujiaxin33/JXCategoryView/blob/master/Document/%E5%B8%B8%E7%94%A8%E5%B1%9E%E6%80%A7%E8%AF%B4%E6%98%8E.md)
|
||||
|
||||
## 更新记录
|
||||
|
||||
- 2018.8.21 发布1.0.0版本,更新内容:使用POP(面向协议编程)重构指示器视图;[迁移指南](https://github.com/pujiaxin33/JXCategoryView/blob/master/Migration/1.0.0.md)
|
||||
- 2018.8.22 发布1.0.1版本,更新内容:删除zoomEnabled,新增titleLabelZoomEnabled、imageZoomEnabled;
|
||||
- 2018.8.23 发布1.0.2版本,更新内容:添加cellWidthZoomEnabled实现腾讯视频效果;
|
||||
- 2018.8.24 发布1.0.3版本,更新内容:添加垂直列表滚动效果、指示器添加verticalMargin属性、JXCategoryViewDelegate代理方法优化;
|
||||
- 2018.9.4 发布1.0.4版本,更新内容:修复bug、添加cell图文混用示例;
|
||||
- 2018.12.19 发布1.1.7版本,更新内容:添加`JXCategoryListContainerView`,高度封装列表逻辑,支持懒加载列表,提升初始化性能;
|
||||
- 2019.1.24 发布1.2.2版本,更新内容:非兼容更新接口`- (BOOL)selectCellAtIndex:(NSInteger)index selectedType:(JXCategoryCellSelectedType)selectedType`,自定义有用到该接口的请及时更新。
|
||||
- 2019.6.21 发布1.3.13版本,更新内容:将`JXCategoryListCollectionContainerView.dataSource`移动到m实现文件,添加`- (instancetype)initWithDataSource:(id<JXCategoryListCollectionContainerViewDataSource>)dataSource`初始化方法。
|
||||
- 2019.7.20 发布1.3.16版本,删除代理方法`- (void)categoryView:(JXCategoryBaseView *)categoryView didClickedItemContentScrollViewTransitionToIndex:(NSInteger)index;`,请使用`contentScrollViewClickTransitionAnimationEnabled`属性。`JXCategoryTitleVerticalZoomView`进行了重构,内容左边距只需要使用`contentEdgeLeft`属性即可。
|
||||
- 2019.9.11 发布1.4.0版本,删除一波被标记为弃用的属性和方法;完善列表的生命周期方法的调用;`JXCategoryListCollectionContainerView`类新增和必须要调用`- (void)scrollingFromLeftIndex:(NSInteger)leftIndex toRightIndex:(NSInteger)rightIndex ratio:(CGFloat)ratio selectedIndex:(NSInteger)selectedIndex`和`- (void)didClickSelectedItemAtIndex:(NSInteger)index`两个方法。
|
||||
- 2019.9.19 发布1.5.0版本,重构列表容器,具体修改请参考[1.5.0版本迁移指南](https://github.com/pujiaxin33/JXCategoryView/blob/master/Migration/1.5.0%E7%89%88%E6%9C%AC%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97.md)
|
||||
|
||||
## 补充
|
||||
|
||||
如果刚开始使用`JXCategoryView`,当开发过程中需要支持某种特性时,请务必先搜索使用文档或者源代码。确认是否已经实现支持了想要的特性。请别不要文档和源代码都没有看,就直接提问,这对于大家都是一种时间浪费。如果没有支持想要的特性,欢迎提Issue讨论,或者自己实现提一个PullRequest。
|
||||
|
||||
该仓库保持随时更新,对于主流新的分类选择效果会第一时间支持。使用过程中,有任何建议或问题,可以通过以下方式联系我:</br>
|
||||
邮箱:317437084@qq.com </br>
|
||||
QQ群: 112440473
|
||||
|
||||
<img src="https://github.com/pujiaxin33/JXExampleImages/blob/master/JXCategoryView/JXCategoryViewQQGroupTwo.JPG" width="300" height="411">
|
||||
|
||||
喜欢就star❤️一下吧
|
||||
|
||||
## License
|
||||
|
||||
JXCategoryView is released under the MIT license.
|
||||
29
Pods/JXCategoryView/Sources/Base/JXCategoryBaseCell.h
generated
Normal file
29
Pods/JXCategoryView/Sources/Base/JXCategoryBaseCell.h
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// JXCategoryBaseCell.h
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryBaseCellModel.h"
|
||||
#import "JXCategoryViewAnimator.h"
|
||||
#import "JXCategoryViewDefines.h"
|
||||
|
||||
@interface JXCategoryBaseCell : UICollectionViewCell
|
||||
|
||||
@property (nonatomic, strong, readonly) JXCategoryBaseCellModel *cellModel;
|
||||
@property (nonatomic, strong, readonly) JXCategoryViewAnimator *animator;
|
||||
|
||||
- (void)initializeViews NS_REQUIRES_SUPER;
|
||||
|
||||
- (void)reloadData:(JXCategoryBaseCellModel *)cellModel NS_REQUIRES_SUPER;
|
||||
|
||||
- (BOOL)checkCanStartSelectedAnimation:(JXCategoryBaseCellModel *)cellModel;
|
||||
|
||||
- (void)addSelectedAnimationBlock:(JXCategoryCellSelectedAnimationBlock)block;
|
||||
|
||||
- (void)startSelectedAnimationIfNeeded:(JXCategoryBaseCellModel *)cellModel;
|
||||
|
||||
@end
|
||||
98
Pods/JXCategoryView/Sources/Base/JXCategoryBaseCell.m
generated
Normal file
98
Pods/JXCategoryView/Sources/Base/JXCategoryBaseCell.m
generated
Normal file
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// JXCategoryBaseCell.m
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryBaseCell.h"
|
||||
#import "RTLManager.h"
|
||||
|
||||
@interface JXCategoryBaseCell ()
|
||||
@property (nonatomic, strong) JXCategoryBaseCellModel *cellModel;
|
||||
@property (nonatomic, strong) JXCategoryViewAnimator *animator;
|
||||
@property (nonatomic, strong) NSMutableArray <JXCategoryCellSelectedAnimationBlock> *animationBlockArray;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryBaseCell
|
||||
|
||||
#pragma mark - Initialize
|
||||
|
||||
- (void)dealloc {
|
||||
[self.animator stop];
|
||||
}
|
||||
|
||||
- (void)prepareForReuse {
|
||||
[super prepareForReuse];
|
||||
|
||||
[self.animator stop];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initializeViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self initializeViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (void)initializeViews {
|
||||
_animationBlockArray = [NSMutableArray array];
|
||||
|
||||
[RTLManager horizontalFlipViewIfNeeded:self];
|
||||
[RTLManager horizontalFlipViewIfNeeded:self.contentView];
|
||||
}
|
||||
|
||||
- (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
|
||||
self.cellModel = cellModel;
|
||||
|
||||
if (cellModel.isSelectedAnimationEnabled) {
|
||||
[self.animationBlockArray removeLastObject];
|
||||
if ([self checkCanStartSelectedAnimation:cellModel]) {
|
||||
self.animator = [[JXCategoryViewAnimator alloc] init];
|
||||
self.animator.duration = cellModel.selectedAnimationDuration;
|
||||
} else {
|
||||
[self.animator stop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)checkCanStartSelectedAnimation:(JXCategoryBaseCellModel *)cellModel {
|
||||
BOOL canStartSelectedAnimation = ((cellModel.selectedType == JXCategoryCellSelectedTypeCode) || (cellModel.selectedType == JXCategoryCellSelectedTypeClick));
|
||||
return canStartSelectedAnimation;
|
||||
}
|
||||
|
||||
- (void)addSelectedAnimationBlock:(JXCategoryCellSelectedAnimationBlock)block {
|
||||
[self.animationBlockArray addObject:block];
|
||||
}
|
||||
|
||||
- (void)startSelectedAnimationIfNeeded:(JXCategoryBaseCellModel *)cellModel {
|
||||
if (cellModel.isSelectedAnimationEnabled && [self checkCanStartSelectedAnimation:cellModel]) {
|
||||
// 需要更新 isTransitionAnimating,用于处理在过滤时,禁止响应点击,避免界面异常。
|
||||
cellModel.transitionAnimating = YES;
|
||||
__weak typeof(self)weakSelf = self;
|
||||
self.animator.progressCallback = ^(CGFloat percent) {
|
||||
for (JXCategoryCellSelectedAnimationBlock block in weakSelf.animationBlockArray) {
|
||||
block(percent);
|
||||
}
|
||||
};
|
||||
self.animator.completeCallback = ^{
|
||||
cellModel.transitionAnimating = NO;
|
||||
[weakSelf.animationBlockArray removeAllObjects];
|
||||
};
|
||||
[self.animator start];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
31
Pods/JXCategoryView/Sources/Base/JXCategoryBaseCellModel.h
generated
Normal file
31
Pods/JXCategoryView/Sources/Base/JXCategoryBaseCellModel.h
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// JXCategoryBaseCellModel.h
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryViewDefines.h"
|
||||
|
||||
@interface JXCategoryBaseCellModel : NSObject
|
||||
|
||||
@property (nonatomic, assign) NSUInteger index;
|
||||
@property (nonatomic, assign) CGFloat cellWidth;
|
||||
@property (nonatomic, assign) CGFloat cellSpacing;
|
||||
@property (nonatomic, assign, getter=isSelected) BOOL selected;
|
||||
|
||||
@property (nonatomic, assign, getter=isCellWidthZoomEnabled) BOOL cellWidthZoomEnabled;
|
||||
@property (nonatomic, assign) CGFloat cellWidthNormalZoomScale;
|
||||
@property (nonatomic, assign) CGFloat cellWidthCurrentZoomScale;
|
||||
@property (nonatomic, assign) CGFloat cellWidthSelectedZoomScale;
|
||||
|
||||
@property (nonatomic, assign, getter=isSelectedAnimationEnabled) BOOL selectedAnimationEnabled;
|
||||
@property (nonatomic, assign) NSTimeInterval selectedAnimationDuration;
|
||||
@property (nonatomic, assign) JXCategoryCellSelectedType selectedType;
|
||||
|
||||
@property (nonatomic, assign, getter=isTransitionAnimating) BOOL transitionAnimating;
|
||||
|
||||
@end
|
||||
13
Pods/JXCategoryView/Sources/Base/JXCategoryBaseCellModel.m
generated
Normal file
13
Pods/JXCategoryView/Sources/Base/JXCategoryBaseCellModel.m
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryBaseCellModel.m
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryBaseCellModel.h"
|
||||
|
||||
@implementation JXCategoryBaseCellModel
|
||||
|
||||
@end
|
||||
222
Pods/JXCategoryView/Sources/Base/JXCategoryBaseView.h
generated
Normal file
222
Pods/JXCategoryView/Sources/Base/JXCategoryBaseView.h
generated
Normal file
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// JXCategoryView.h
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryBaseCell.h"
|
||||
#import "JXCategoryBaseCellModel.h"
|
||||
#import "JXCategoryCollectionView.h"
|
||||
#import "JXCategoryViewDefines.h"
|
||||
|
||||
@class JXCategoryBaseView;
|
||||
|
||||
@protocol JXCategoryViewListContainer <NSObject>
|
||||
- (void)setDefaultSelectedIndex:(NSInteger)index;
|
||||
- (UIScrollView *)contentScrollView;
|
||||
- (void)reloadData;
|
||||
- (void)didClickSelectedItemAtIndex:(NSInteger)index;
|
||||
@end
|
||||
|
||||
@protocol JXCategoryViewDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
//为什么会把选中代理分为三个,因为有时候只关心点击选中的,有时候只关心滚动选中的,有时候只关心选中。所以具体情况,使用对应方法。
|
||||
/**
|
||||
点击选中或者滚动选中都会调用该方法。适用于只关心选中事件,不关心具体是点击还是滚动选中的。
|
||||
|
||||
@param categoryView categoryView对象
|
||||
@param index 选中的index
|
||||
*/
|
||||
- (void)categoryView:(JXCategoryBaseView *)categoryView didSelectedItemAtIndex:(NSInteger)index;
|
||||
|
||||
/**
|
||||
点击选中的情况才会调用该方法
|
||||
|
||||
@param categoryView categoryView对象
|
||||
@param index 选中的index
|
||||
*/
|
||||
- (void)categoryView:(JXCategoryBaseView *)categoryView didClickSelectedItemAtIndex:(NSInteger)index;
|
||||
|
||||
/**
|
||||
滚动选中的情况才会调用该方法
|
||||
|
||||
@param categoryView categoryView对象
|
||||
@param index 选中的index
|
||||
*/
|
||||
- (void)categoryView:(JXCategoryBaseView *)categoryView didScrollSelectedItemAtIndex:(NSInteger)index;
|
||||
|
||||
/**
|
||||
控制能否点击Item
|
||||
|
||||
@param categoryView categoryView对象
|
||||
@param index 准备点击的index
|
||||
@return 能否点击
|
||||
*/
|
||||
- (BOOL)categoryView:(JXCategoryBaseView *)categoryView canClickItemAtIndex:(NSInteger)index;
|
||||
|
||||
/**
|
||||
正在滚动中的回调
|
||||
|
||||
@param categoryView categoryView对象
|
||||
@param leftIndex 正在滚动中,相对位置处于左边的index
|
||||
@param rightIndex 正在滚动中,相对位置处于右边的index
|
||||
@param ratio 从左往右计算的百分比
|
||||
*/
|
||||
- (void)categoryView:(JXCategoryBaseView *)categoryView scrollingFromLeftIndex:(NSInteger)leftIndex toRightIndex:(NSInteger)rightIndex ratio:(CGFloat)ratio;
|
||||
|
||||
@end
|
||||
|
||||
@interface JXCategoryBaseView : UIView
|
||||
|
||||
@property (nonatomic, strong, readonly) JXCategoryCollectionView *collectionView;
|
||||
|
||||
@property (nonatomic, strong) NSArray <JXCategoryBaseCellModel *> *dataSource;
|
||||
|
||||
@property (nonatomic, weak) id<JXCategoryViewDelegate> delegate;
|
||||
|
||||
/**
|
||||
高封装度的列表容器,使用该类可以让列表拥有完成的生命周期、自动同步defaultSelectedIndex、自动调用reloadData。
|
||||
*/
|
||||
@property (nonatomic, weak) id<JXCategoryViewListContainer> listContainer;
|
||||
|
||||
/**
|
||||
推荐使用封装度更高的listContainer属性。如果使用contentScrollView请参考`LoadDataListCustomViewController`使用示例。
|
||||
*/
|
||||
@property (nonatomic, strong) UIScrollView *contentScrollView;
|
||||
|
||||
@property (nonatomic, assign) NSInteger defaultSelectedIndex; //修改初始化的时候默认选择的index
|
||||
|
||||
@property (nonatomic, assign, readonly) NSInteger selectedIndex;
|
||||
|
||||
@property (nonatomic, assign, getter=isContentScrollViewClickTransitionAnimationEnabled) BOOL contentScrollViewClickTransitionAnimationEnabled; //点击cell进行contentScrollView切换时是否需要动画。默认为YES
|
||||
|
||||
@property (nonatomic, assign) CGFloat contentEdgeInsetLeft; //整体内容的左边距,默认JXCategoryViewAutomaticDimension(等于cellSpacing)
|
||||
|
||||
@property (nonatomic, assign) CGFloat contentEdgeInsetRight; //整体内容的右边距,默认JXCategoryViewAutomaticDimension(等于cellSpacing)
|
||||
|
||||
@property (nonatomic, assign) CGFloat cellWidth; //默认JXCategoryViewAutomaticDimension
|
||||
|
||||
@property (nonatomic, assign) CGFloat cellWidthIncrement; //cell宽度补偿。默认:0
|
||||
|
||||
@property (nonatomic, assign) CGFloat cellSpacing; //cell之间的间距,默认20
|
||||
|
||||
@property (nonatomic, assign, getter=isAverageCellSpacingEnabled) BOOL averageCellSpacingEnabled; //当collectionView.contentSize.width小于JXCategoryBaseView的宽度,是否将cellSpacing均分。默认为YES。
|
||||
|
||||
//cell宽度是否缩放
|
||||
@property (nonatomic, assign, getter=isCellWidthZoomEnabled) BOOL cellWidthZoomEnabled; //默认为NO
|
||||
|
||||
@property (nonatomic, assign, getter=isCellWidthZoomScrollGradientEnabled) BOOL cellWidthZoomScrollGradientEnabled; //手势滚动过程中,是否需要更新cell的宽度。默认为YES
|
||||
|
||||
@property (nonatomic, assign) CGFloat cellWidthZoomScale; //默认1.2,cellWidthZoomEnabled为YES才生效
|
||||
|
||||
@property (nonatomic, assign, getter=isSelectedAnimationEnabled) BOOL selectedAnimationEnabled; //是否开启点击或代码选中动画。默认为NO。自定义的cell选中动画需要自己实现。(仅点击或调用selectItemAtIndex选中才有效,滚动选中无效)
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval selectedAnimationDuration; //cell选中动画的时间。默认0.25
|
||||
|
||||
/**
|
||||
选中目标index的item
|
||||
|
||||
@param index 目标index
|
||||
*/
|
||||
- (void)selectItemAtIndex:(NSInteger)index;
|
||||
|
||||
/**
|
||||
初始化的时候无需调用。比如页面初始化之后,根据网络接口异步回调回来数据,重新配置categoryView,需要调用该方法进行刷新。
|
||||
*/
|
||||
- (void)reloadData;
|
||||
|
||||
/**
|
||||
重新配置categoryView但是不需要reload listContainer。特殊情况是该方法。
|
||||
*/
|
||||
- (void)reloadDataWithoutListContainer;
|
||||
|
||||
/**
|
||||
刷新指定的index的cell
|
||||
内部会触发`- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index`方法进行cellModel刷新
|
||||
|
||||
@param index 指定cell的index
|
||||
*/
|
||||
- (void)reloadCellAtIndex:(NSInteger)index;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface JXCategoryBaseView (UISubclassingBaseHooks)
|
||||
|
||||
/**
|
||||
获取目标cell当前的frame,反应当前真实的frame受到cellWidthSelectedZoomScale的影响。
|
||||
*/
|
||||
- (CGRect)getTargetCellFrame:(NSInteger)targetIndex;
|
||||
|
||||
/**
|
||||
获取目标cell的选中时的frame,其他cell的状态都当做普通状态处理。
|
||||
*/
|
||||
- (CGRect)getTargetSelectedCellFrame:(NSInteger)targetIndex selectedType:(JXCategoryCellSelectedType)selectedType;
|
||||
- (void)initializeData NS_REQUIRES_SUPER;
|
||||
- (void)initializeViews NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
reloadData方法调用,重新生成数据源赋值到self.dataSource
|
||||
*/
|
||||
- (void)refreshDataSource;
|
||||
|
||||
/**
|
||||
reloadData方法调用,根据数据源重新刷新状态;
|
||||
*/
|
||||
- (void)refreshState NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
选中某个item时,刷新将要选中与取消选中的cellModel
|
||||
|
||||
@param selectedCellModel 将要选中的cellModel
|
||||
@param unselectedCellModel 取消选中的cellModel
|
||||
*/
|
||||
- (void)refreshSelectedCellModel:(JXCategoryBaseCellModel *)selectedCellModel unselectedCellModel:(JXCategoryBaseCellModel *)unselectedCellModel NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
关联的contentScrollView的contentOffset发生了改变
|
||||
|
||||
@param contentOffset 偏移量
|
||||
*/
|
||||
- (void)contentOffsetOfContentScrollViewDidChanged:(CGPoint)contentOffset NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
选中某一个item的时候调用,该方法用于子类重载。
|
||||
如果外部要选中某个index,请使用`- (void)selectItemAtIndex:(NSUInteger)index;`
|
||||
|
||||
@param index 选中的index
|
||||
@param selectedType JXCategoryCellSelectedType
|
||||
@return 返回值为NO,表示触发内部某些判断(点击了同一个cell),子类无需后续操作。
|
||||
*/
|
||||
- (BOOL)selectCellAtIndex:(NSInteger)index selectedType:(JXCategoryCellSelectedType)selectedType NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
reloadData时,返回每个cell的宽度
|
||||
|
||||
@param index 目标index
|
||||
@return cellWidth
|
||||
*/
|
||||
- (CGFloat)preferredCellWidthAtIndex:(NSInteger)index;
|
||||
|
||||
/**
|
||||
返回自定义cell的class
|
||||
|
||||
@return cell class
|
||||
*/
|
||||
- (Class)preferredCellClass;
|
||||
|
||||
/**
|
||||
refreshState时调用,重置cellModel的状态
|
||||
|
||||
@param cellModel 待重置的cellModel
|
||||
@param index cellModel在数组中的index
|
||||
*/
|
||||
- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index NS_REQUIRES_SUPER;
|
||||
|
||||
@end
|
||||
697
Pods/JXCategoryView/Sources/Base/JXCategoryBaseView.m
generated
Normal file
697
Pods/JXCategoryView/Sources/Base/JXCategoryBaseView.m
generated
Normal file
@@ -0,0 +1,697 @@
|
||||
//
|
||||
// JXCategoryBaseView.m
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryBaseView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
#import "JXCategoryViewAnimator.h"
|
||||
#import "RTLManager.h"
|
||||
|
||||
struct DelegateFlags {
|
||||
unsigned int didSelectedItemAtIndexFlag : 1;
|
||||
unsigned int didClickSelectedItemAtIndexFlag : 1;
|
||||
unsigned int didScrollSelectedItemAtIndexFlag : 1;
|
||||
unsigned int canClickItemAtIndexFlag : 1;
|
||||
unsigned int scrollingFromLeftIndexToRightIndexFlag : 1;
|
||||
};
|
||||
|
||||
@interface JXCategoryBaseView () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
|
||||
|
||||
@property (nonatomic, strong) JXCategoryCollectionView *collectionView;
|
||||
@property (nonatomic, assign) struct DelegateFlags delegateFlags;
|
||||
@property (nonatomic, assign) NSInteger selectedIndex;
|
||||
@property (nonatomic, assign) CGFloat innerCellSpacing;
|
||||
@property (nonatomic, assign) CGPoint lastContentViewContentOffset;
|
||||
@property (nonatomic, strong) JXCategoryViewAnimator *animator;
|
||||
// 正在滚动中的目标index。用于处理正在滚动列表的时候,立即点击item,会导致界面显示异常。
|
||||
@property (nonatomic, assign) NSInteger scrollingTargetIndex;
|
||||
@property (nonatomic, assign, getter=isNeedReloadByBecomeActive) BOOL needReloadByBecomeActive;
|
||||
@property (nonatomic, assign, getter=isFirstLayoutSubviews) BOOL firstLayoutSubviews;
|
||||
@property (nonatomic, assign, getter=isNeedConfigAutomaticallyAdjustsScrollViewInsets) BOOL needConfigAutomaticallyAdjustsScrollViewInsets;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JXCategoryBaseView
|
||||
|
||||
- (void)dealloc {
|
||||
if (self.contentScrollView) {
|
||||
[self.contentScrollView removeObserver:self forKeyPath:@"contentOffset"];
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[self.animator stop];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initializeData];
|
||||
[self initializeViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self initializeData];
|
||||
[self initializeViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)willMoveToSuperview:(UIView *)newSuperview {
|
||||
[super willMoveToSuperview:newSuperview];
|
||||
|
||||
[self configAutomaticallyAdjustsScrollViewInsets:newSuperview];
|
||||
}
|
||||
|
||||
- (void)reloadData {
|
||||
[self reloadDataWithoutListContainer];
|
||||
[self.listContainer reloadData];
|
||||
}
|
||||
|
||||
- (void)reloadDataWithoutListContainer {
|
||||
[self refreshDataSource];
|
||||
[self refreshState];
|
||||
[self.collectionView.collectionViewLayout invalidateLayout];
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
|
||||
- (void)reloadCellAtIndex:(NSInteger)index {
|
||||
if (index < 0 || index >= self.dataSource.count) {
|
||||
return;
|
||||
}
|
||||
JXCategoryBaseCellModel *cellModel = self.dataSource[index];
|
||||
cellModel.selectedType = JXCategoryCellSelectedTypeUnknown;
|
||||
[self refreshCellModel:cellModel index:index];
|
||||
JXCategoryBaseCell *cell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
|
||||
[cell reloadData:cellModel];
|
||||
}
|
||||
|
||||
- (void)selectItemAtIndex:(NSInteger)index {
|
||||
[self selectCellAtIndex:index selectedType:JXCategoryCellSelectedTypeCode];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
//部分使用者为了适配不同的手机屏幕尺寸,JXCategoryView的宽高比要求保持一样,所以它的高度就会因为不同宽度的屏幕而不一样。计算出来的高度,有时候会是位数很长的浮点数,如果把这个高度设置给UICollectionView就会触发内部的一个错误。所以,为了规避这个问题,在这里对高度统一向下取整。
|
||||
//如果向下取整导致了你的页面异常,请自己重新设置JXCategoryView的高度,保证为整数即可。
|
||||
CGRect targetFrame = CGRectMake(0, 0, self.bounds.size.width, floor(self.bounds.size.height));
|
||||
if (self.isFirstLayoutSubviews) {
|
||||
if (self.bounds.size.width == 0 || self.bounds.size.height == 0) {
|
||||
return;
|
||||
}
|
||||
if (self.isNeedConfigAutomaticallyAdjustsScrollViewInsets) {
|
||||
[self configAutomaticallyAdjustsScrollViewInsets:self.superview];
|
||||
}
|
||||
self.firstLayoutSubviews = NO;
|
||||
self.collectionView.frame = targetFrame;
|
||||
[self reloadDataWithoutListContainer];
|
||||
}else {
|
||||
if (!CGRectEqualToRect(self.collectionView.frame, targetFrame)) {
|
||||
self.collectionView.frame = targetFrame;
|
||||
[self refreshState];
|
||||
[self.collectionView.collectionViewLayout invalidateLayout];
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Setter
|
||||
|
||||
- (void)setDelegate:(id<JXCategoryViewDelegate>)delegate {
|
||||
_delegate = delegate;
|
||||
|
||||
_delegateFlags.didSelectedItemAtIndexFlag = [delegate respondsToSelector:@selector(categoryView:didSelectedItemAtIndex:)];
|
||||
_delegateFlags.didClickSelectedItemAtIndexFlag = [delegate respondsToSelector:@selector(categoryView:didClickSelectedItemAtIndex:)];
|
||||
_delegateFlags.didScrollSelectedItemAtIndexFlag = [delegate respondsToSelector:@selector(categoryView:didScrollSelectedItemAtIndex:)];
|
||||
_delegateFlags.canClickItemAtIndexFlag = [delegate respondsToSelector:@selector(categoryView:canClickItemAtIndex:)];
|
||||
_delegateFlags.scrollingFromLeftIndexToRightIndexFlag = [delegate respondsToSelector:@selector(categoryView:scrollingFromLeftIndex:toRightIndex:ratio:)];
|
||||
}
|
||||
|
||||
- (void)setDefaultSelectedIndex:(NSInteger)defaultSelectedIndex {
|
||||
_defaultSelectedIndex = defaultSelectedIndex;
|
||||
|
||||
self.selectedIndex = defaultSelectedIndex;
|
||||
[self.listContainer setDefaultSelectedIndex:defaultSelectedIndex];
|
||||
}
|
||||
|
||||
- (void)setContentScrollView:(UIScrollView *)contentScrollView {
|
||||
if (_contentScrollView != nil) {
|
||||
[_contentScrollView removeObserver:self forKeyPath:@"contentOffset"];
|
||||
}
|
||||
_contentScrollView = contentScrollView;
|
||||
|
||||
self.contentScrollView.scrollsToTop = NO;
|
||||
[self.contentScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
|
||||
}
|
||||
|
||||
- (void)setListContainer:(id<JXCategoryViewListContainer>)listContainer {
|
||||
_listContainer = listContainer;
|
||||
[listContainer setDefaultSelectedIndex:self.defaultSelectedIndex];
|
||||
self.contentScrollView = [listContainer contentScrollView];
|
||||
}
|
||||
|
||||
#pragma mark - <UICollectionViewDataSource, UICollectionViewDelegate>
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return self.dataSource.count;
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([self preferredCellClass]) forIndexPath:indexPath];
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
JXCategoryBaseCellModel *cellModel = self.dataSource[indexPath.item];
|
||||
cellModel.selectedType = JXCategoryCellSelectedTypeUnknown;
|
||||
[(JXCategoryBaseCell *)cell reloadData:cellModel];
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
BOOL isTransitionAnimating = NO;
|
||||
for (JXCategoryBaseCellModel *cellModel in self.dataSource) {
|
||||
if (cellModel.isTransitionAnimating) {
|
||||
isTransitionAnimating = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isTransitionAnimating) {
|
||||
//当前没有正在过渡的item,才允许点击选中
|
||||
[self clickSelectItemAtIndex:indexPath.row];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - <UICollectionViewDelegateFlowLayout>
|
||||
|
||||
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
|
||||
return UIEdgeInsetsMake(0, [self getContentEdgeInsetLeft], 0, [self getContentEdgeInsetRight]);
|
||||
}
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return CGSizeMake(self.dataSource[indexPath.item].cellWidth, self.collectionView.bounds.size.height);
|
||||
}
|
||||
|
||||
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
|
||||
return self.innerCellSpacing;
|
||||
}
|
||||
|
||||
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
|
||||
return self.innerCellSpacing;
|
||||
}
|
||||
|
||||
#pragma mark - KVO
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
|
||||
if ([keyPath isEqualToString:@"contentOffset"]) {
|
||||
CGPoint contentOffset = [change[NSKeyValueChangeNewKey] CGPointValue];
|
||||
if ((self.contentScrollView.isTracking || self.contentScrollView.isDecelerating)) {
|
||||
//只处理用户滚动的情况
|
||||
[self contentOffsetOfContentScrollViewDidChanged:contentOffset];
|
||||
}
|
||||
self.lastContentViewContentOffset = contentOffset;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)configAutomaticallyAdjustsScrollViewInsets:(UIView *)view {
|
||||
UIResponder *next = view;
|
||||
while (next != nil) {
|
||||
if ([next isKindOfClass:[UIViewController class]]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
((UIViewController *)next).automaticallyAdjustsScrollViewInsets = NO;
|
||||
#pragma clang diagnostic pop
|
||||
self.needConfigAutomaticallyAdjustsScrollViewInsets = NO;
|
||||
break;
|
||||
}
|
||||
next = next.nextResponder;
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)getContentEdgeInsetLeft {
|
||||
if (self.contentEdgeInsetLeft == JXCategoryViewAutomaticDimension) {
|
||||
return self.innerCellSpacing;
|
||||
}
|
||||
return self.contentEdgeInsetLeft;
|
||||
}
|
||||
|
||||
- (CGFloat)getContentEdgeInsetRight {
|
||||
if (self.contentEdgeInsetRight == JXCategoryViewAutomaticDimension) {
|
||||
return self.innerCellSpacing;
|
||||
}
|
||||
return self.contentEdgeInsetRight;
|
||||
}
|
||||
|
||||
- (CGFloat)getCellWidthAtIndex:(NSInteger)index {
|
||||
return [self preferredCellWidthAtIndex:index] + self.cellWidthIncrement;
|
||||
}
|
||||
|
||||
- (void)clickSelectItemAtIndex:(NSInteger)index {
|
||||
if (self.delegateFlags.canClickItemAtIndexFlag && ![self.delegate categoryView:self canClickItemAtIndex:index]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self selectCellAtIndex:index selectedType:JXCategoryCellSelectedTypeClick];
|
||||
}
|
||||
|
||||
- (void)scrollSelectItemAtIndex:(NSInteger)index {
|
||||
[self selectCellAtIndex:index selectedType:JXCategoryCellSelectedTypeScroll];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification {
|
||||
if (self.isNeedReloadByBecomeActive) {
|
||||
self.needReloadByBecomeActive = NO;
|
||||
[self reloadData];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation JXCategoryBaseView (UISubclassingBaseHooks)
|
||||
|
||||
- (CGRect)getTargetCellFrame:(NSInteger)targetIndex {
|
||||
CGFloat x = [self getContentEdgeInsetLeft];
|
||||
for (int i = 0; i < targetIndex; i ++) {
|
||||
JXCategoryBaseCellModel *cellModel = self.dataSource[i];
|
||||
CGFloat cellWidth;
|
||||
if (cellModel.isTransitionAnimating && cellModel.isCellWidthZoomEnabled) {
|
||||
//正在进行动画的时候,cellWidthCurrentZoomScale是随着动画渐变的,而没有立即更新到目标值
|
||||
if (cellModel.isSelected) {
|
||||
cellWidth = [self getCellWidthAtIndex:cellModel.index]*cellModel.cellWidthSelectedZoomScale;
|
||||
}else {
|
||||
cellWidth = [self getCellWidthAtIndex:cellModel.index]*cellModel.cellWidthNormalZoomScale;
|
||||
}
|
||||
}else {
|
||||
cellWidth = cellModel.cellWidth;
|
||||
}
|
||||
x += cellWidth + self.innerCellSpacing;
|
||||
}
|
||||
CGFloat width;
|
||||
JXCategoryBaseCellModel *selectedCellModel = self.dataSource[targetIndex];
|
||||
if (selectedCellModel.isTransitionAnimating && selectedCellModel.isCellWidthZoomEnabled) {
|
||||
width = [self getCellWidthAtIndex:selectedCellModel.index]*selectedCellModel.cellWidthSelectedZoomScale;
|
||||
}else {
|
||||
width = selectedCellModel.cellWidth;
|
||||
}
|
||||
return CGRectMake(x, 0, width, self.bounds.size.height);
|
||||
}
|
||||
|
||||
- (CGRect)getTargetSelectedCellFrame:(NSInteger)targetIndex selectedType:(JXCategoryCellSelectedType)selectedType {
|
||||
CGFloat x = [self getContentEdgeInsetLeft];
|
||||
for (int i = 0; i < targetIndex; i ++) {
|
||||
JXCategoryBaseCellModel *cellModel = self.dataSource[i];
|
||||
x += [self getCellWidthAtIndex:cellModel.index] + self.innerCellSpacing;
|
||||
}
|
||||
CGFloat cellWidth = 0;
|
||||
JXCategoryBaseCellModel *selectedCellModel = self.dataSource[targetIndex];
|
||||
if (selectedCellModel.cellWidthZoomEnabled) {
|
||||
cellWidth = [self getCellWidthAtIndex:targetIndex]*selectedCellModel.cellWidthSelectedZoomScale;
|
||||
}else {
|
||||
cellWidth = [self getCellWidthAtIndex:targetIndex];
|
||||
}
|
||||
return CGRectMake(x, 0, cellWidth, self.bounds.size.height);
|
||||
}
|
||||
|
||||
- (void)initializeData {
|
||||
_firstLayoutSubviews = YES;
|
||||
_dataSource = [NSMutableArray array];
|
||||
_selectedIndex = 0;
|
||||
_cellWidth = JXCategoryViewAutomaticDimension;
|
||||
_cellWidthIncrement = 0;
|
||||
_cellSpacing = 20;
|
||||
_averageCellSpacingEnabled = YES;
|
||||
_cellWidthZoomEnabled = NO;
|
||||
_cellWidthZoomScale = 1.2;
|
||||
_cellWidthZoomScrollGradientEnabled = YES;
|
||||
_contentEdgeInsetLeft = JXCategoryViewAutomaticDimension;
|
||||
_contentEdgeInsetRight = JXCategoryViewAutomaticDimension;
|
||||
_lastContentViewContentOffset = CGPointZero;
|
||||
_selectedAnimationEnabled = NO;
|
||||
_selectedAnimationDuration = 0.25;
|
||||
_scrollingTargetIndex = -1;
|
||||
_contentScrollViewClickTransitionAnimationEnabled = YES;
|
||||
_needReloadByBecomeActive = NO;
|
||||
}
|
||||
|
||||
- (void)initializeViews {
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
_collectionView = [[JXCategoryCollectionView alloc] initWithFrame:self.bounds collectionViewLayout:layout];
|
||||
self.collectionView.backgroundColor = [UIColor clearColor];
|
||||
self.collectionView.showsHorizontalScrollIndicator = NO;
|
||||
self.collectionView.showsVerticalScrollIndicator = NO;
|
||||
self.collectionView.scrollsToTop = NO;
|
||||
self.collectionView.dataSource = self;
|
||||
self.collectionView.delegate = self;
|
||||
[self.collectionView registerClass:[self preferredCellClass] forCellWithReuseIdentifier:NSStringFromClass([self preferredCellClass])];
|
||||
if (@available(iOS 10.0, *)) {
|
||||
self.collectionView.prefetchingEnabled = NO;
|
||||
}
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if ([self.collectionView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
|
||||
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
}
|
||||
if ([RTLManager supportRTL]) {
|
||||
self.collectionView.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
|
||||
[RTLManager horizontalFlipView:self.collectionView];
|
||||
}
|
||||
[self addSubview:self.collectionView];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)refreshDataSource {}
|
||||
|
||||
- (void)refreshState {
|
||||
if (self.selectedIndex < 0 || self.selectedIndex >= self.dataSource.count) {
|
||||
self.defaultSelectedIndex = 0;
|
||||
}
|
||||
|
||||
self.innerCellSpacing = self.cellSpacing;
|
||||
//总的内容宽度(左边距+cell总宽度+总cellSpacing+右边距)
|
||||
__block CGFloat totalItemWidth = [self getContentEdgeInsetLeft];
|
||||
//总的cell宽度
|
||||
CGFloat totalCellWidth = 0;
|
||||
for (int i = 0; i < self.dataSource.count; i++) {
|
||||
JXCategoryBaseCellModel *cellModel = self.dataSource[i];
|
||||
cellModel.index = i;
|
||||
cellModel.cellWidthZoomEnabled = self.cellWidthZoomEnabled;
|
||||
cellModel.cellWidthNormalZoomScale = 1;
|
||||
cellModel.cellWidthSelectedZoomScale = self.cellWidthZoomScale;
|
||||
cellModel.selectedAnimationEnabled = self.selectedAnimationEnabled;
|
||||
cellModel.selectedAnimationDuration = self.selectedAnimationDuration;
|
||||
cellModel.cellSpacing = self.innerCellSpacing;
|
||||
if (i == self.selectedIndex) {
|
||||
cellModel.selected = YES;
|
||||
cellModel.cellWidthCurrentZoomScale = cellModel.cellWidthSelectedZoomScale;
|
||||
}else {
|
||||
cellModel.selected = NO;
|
||||
cellModel.cellWidthCurrentZoomScale = cellModel.cellWidthNormalZoomScale;
|
||||
}
|
||||
if (self.isCellWidthZoomEnabled) {
|
||||
cellModel.cellWidth = [self getCellWidthAtIndex:i]*cellModel.cellWidthCurrentZoomScale;
|
||||
}else {
|
||||
cellModel.cellWidth = [self getCellWidthAtIndex:i];
|
||||
}
|
||||
totalCellWidth += cellModel.cellWidth;
|
||||
if (i == self.dataSource.count - 1) {
|
||||
totalItemWidth += cellModel.cellWidth + [self getContentEdgeInsetRight];
|
||||
}else {
|
||||
totalItemWidth += cellModel.cellWidth + self.innerCellSpacing;
|
||||
}
|
||||
[self refreshCellModel:cellModel index:i];
|
||||
}
|
||||
|
||||
if (self.isAverageCellSpacingEnabled && totalItemWidth < self.bounds.size.width) {
|
||||
//如果总的内容宽度都没有超过视图宽度,就将cellSpacing等分
|
||||
NSInteger cellSpacingItemCount = self.dataSource.count - 1;
|
||||
CGFloat totalCellSpacingWidth = self.bounds.size.width - totalCellWidth;
|
||||
//如果内容左边距是Automatic,就加1
|
||||
if (self.contentEdgeInsetLeft == JXCategoryViewAutomaticDimension) {
|
||||
cellSpacingItemCount += 1;
|
||||
}else {
|
||||
totalCellSpacingWidth -= self.contentEdgeInsetLeft;
|
||||
}
|
||||
//如果内容右边距是Automatic,就加1
|
||||
if (self.contentEdgeInsetRight == JXCategoryViewAutomaticDimension) {
|
||||
cellSpacingItemCount += 1;
|
||||
}else {
|
||||
totalCellSpacingWidth -= self.contentEdgeInsetRight;
|
||||
}
|
||||
|
||||
CGFloat cellSpacing = 0;
|
||||
if (cellSpacingItemCount > 0) {
|
||||
cellSpacing = totalCellSpacingWidth/cellSpacingItemCount;
|
||||
}
|
||||
self.innerCellSpacing = cellSpacing;
|
||||
[self.dataSource enumerateObjectsUsingBlock:^(JXCategoryBaseCellModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
obj.cellSpacing = self.innerCellSpacing;
|
||||
}];
|
||||
}
|
||||
|
||||
//---------------------定位collectionView到当前选中的位置----------------------
|
||||
//因为初始化的时候,collectionView并没有初始化完,cell都没有被加载出来。只有自己手动计算当前选中的index的位置,然后更新到contentOffset
|
||||
__block CGFloat frameXOfSelectedCell = [self getContentEdgeInsetLeft];
|
||||
__block CGFloat selectedCellWidth = 0;
|
||||
totalItemWidth = [self getContentEdgeInsetLeft];
|
||||
[self.dataSource enumerateObjectsUsingBlock:^(JXCategoryBaseCellModel * cellModel, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if (idx < self.selectedIndex) {
|
||||
frameXOfSelectedCell += cellModel.cellWidth + self.innerCellSpacing;
|
||||
}else if (idx == self.selectedIndex) {
|
||||
selectedCellWidth = cellModel.cellWidth;
|
||||
}
|
||||
if (idx == self.dataSource.count - 1) {
|
||||
totalItemWidth += cellModel.cellWidth + [self getContentEdgeInsetRight];
|
||||
}else {
|
||||
totalItemWidth += cellModel.cellWidth + self.innerCellSpacing;
|
||||
}
|
||||
}];
|
||||
|
||||
CGFloat minX = 0;
|
||||
CGFloat maxX = totalItemWidth - self.bounds.size.width;
|
||||
CGFloat targetX = frameXOfSelectedCell - self.bounds.size.width/2.0 + selectedCellWidth/2.0;
|
||||
CGPoint collectionViewContentOffset = self.collectionView.contentOffset;
|
||||
collectionViewContentOffset.x = MAX(MIN(maxX, targetX), minX);
|
||||
|
||||
[self.collectionView setContentOffset:collectionViewContentOffset
|
||||
animated:NO];
|
||||
//---------------------定位collectionView到当前选中的位置----------------------
|
||||
|
||||
if (CGRectEqualToRect(self.contentScrollView.frame, CGRectZero) && self.contentScrollView.superview != nil) {
|
||||
//某些情况系统会出现JXCategoryView先布局,contentScrollView后布局。就会导致下面指定defaultSelectedIndex失效,所以发现contentScrollView的frame为zero时,强行触发其父视图链里面已经有frame的一个父视图的layoutSubviews方法。
|
||||
//比如JXSegmentedListContainerView会将contentScrollView包裹起来使用,该情况需要JXSegmentedListContainerView.superView触发布局更新
|
||||
UIView *parentView = self.contentScrollView.superview;
|
||||
while (parentView != nil && CGRectEqualToRect(parentView.frame, CGRectZero)) {
|
||||
parentView = parentView.superview;
|
||||
}
|
||||
[parentView setNeedsLayout];
|
||||
[parentView layoutIfNeeded];
|
||||
}
|
||||
//将contentScrollView的contentOffset定位到当前选中index的位置
|
||||
CGPoint contentScrollViewContentOffset = self.contentScrollView.contentOffset;
|
||||
contentScrollViewContentOffset.x = self.selectedIndex*self.contentScrollView.bounds.size.width;
|
||||
[self.contentScrollView setContentOffset:contentScrollViewContentOffset animated:NO];
|
||||
}
|
||||
|
||||
- (BOOL)selectCellAtIndex:(NSInteger)targetIndex selectedType:(JXCategoryCellSelectedType)selectedType {
|
||||
if (targetIndex < 0 || targetIndex >= self.dataSource.count) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
self.needReloadByBecomeActive = NO;
|
||||
if (self.selectedIndex == targetIndex) {
|
||||
//目标index和当前选中的index相等,就不需要处理后续的选中更新逻辑,只需要回调代理方法即可。
|
||||
if (selectedType == JXCategoryCellSelectedTypeCode) {
|
||||
[self.listContainer didClickSelectedItemAtIndex:targetIndex];
|
||||
}else if (selectedType == JXCategoryCellSelectedTypeClick) {
|
||||
[self.listContainer didClickSelectedItemAtIndex:targetIndex];
|
||||
if (self.delegateFlags.didClickSelectedItemAtIndexFlag) {
|
||||
[self.delegate categoryView:self didClickSelectedItemAtIndex:targetIndex];
|
||||
}
|
||||
}else if (selectedType == JXCategoryCellSelectedTypeScroll) {
|
||||
if (self.delegateFlags.didScrollSelectedItemAtIndexFlag) {
|
||||
[self.delegate categoryView:self didScrollSelectedItemAtIndex:targetIndex];
|
||||
}
|
||||
}
|
||||
if (self.delegateFlags.didSelectedItemAtIndexFlag) {
|
||||
[self.delegate categoryView:self didSelectedItemAtIndex:targetIndex];
|
||||
}
|
||||
self.scrollingTargetIndex = -1;
|
||||
return NO;
|
||||
}
|
||||
|
||||
//通知子类刷新当前选中的和将要选中的cellModel
|
||||
JXCategoryBaseCellModel *lastCellModel = self.dataSource[self.selectedIndex];
|
||||
lastCellModel.selectedType = selectedType;
|
||||
JXCategoryBaseCellModel *selectedCellModel = self.dataSource[targetIndex];
|
||||
selectedCellModel.selectedType = selectedType;
|
||||
[self refreshSelectedCellModel:selectedCellModel unselectedCellModel:lastCellModel];
|
||||
|
||||
//刷新当前选中的和将要选中的cell
|
||||
JXCategoryBaseCell *lastCell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.selectedIndex inSection:0]];
|
||||
[lastCell reloadData:lastCellModel];
|
||||
JXCategoryBaseCell *selectedCell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0]];
|
||||
[selectedCell reloadData:selectedCellModel];
|
||||
|
||||
if (self.scrollingTargetIndex != -1 && self.scrollingTargetIndex != targetIndex) {
|
||||
JXCategoryBaseCellModel *scrollingTargetCellModel = self.dataSource[self.scrollingTargetIndex];
|
||||
scrollingTargetCellModel.selected = NO;
|
||||
scrollingTargetCellModel.selectedType = selectedType;
|
||||
[self refreshSelectedCellModel:selectedCellModel unselectedCellModel:scrollingTargetCellModel];
|
||||
JXCategoryBaseCell *scrollingTargetCell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.scrollingTargetIndex inSection:0]];
|
||||
[scrollingTargetCell reloadData:scrollingTargetCellModel];
|
||||
}
|
||||
|
||||
if (self.isCellWidthZoomEnabled) {
|
||||
[self.collectionView.collectionViewLayout invalidateLayout];
|
||||
//延时为了解决cellwidth变化,点击最后几个cell,scrollToItem会出现位置偏移bu。需要等cellWidth动画渐变结束后再滚动到index的cell位置。
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.selectedAnimationDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
|
||||
});
|
||||
} else {
|
||||
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
|
||||
}
|
||||
|
||||
if (selectedType == JXCategoryCellSelectedTypeClick ||
|
||||
selectedType == JXCategoryCellSelectedTypeCode) {
|
||||
CGPoint offset = self.contentScrollView.contentOffset;
|
||||
offset.x =
|
||||
targetIndex*self.contentScrollView.bounds.size.width;
|
||||
[self.contentScrollView setContentOffset:offset
|
||||
animated:self.isContentScrollViewClickTransitionAnimationEnabled];
|
||||
}
|
||||
|
||||
self.selectedIndex = targetIndex;
|
||||
if (selectedType == JXCategoryCellSelectedTypeCode) {
|
||||
[self.listContainer didClickSelectedItemAtIndex:targetIndex];
|
||||
} else if (selectedType == JXCategoryCellSelectedTypeClick) {
|
||||
[self.listContainer didClickSelectedItemAtIndex:targetIndex];
|
||||
if (self.delegateFlags.didClickSelectedItemAtIndexFlag) {
|
||||
[self.delegate categoryView:self didClickSelectedItemAtIndex:targetIndex];
|
||||
}
|
||||
} else if(selectedType == JXCategoryCellSelectedTypeScroll) {
|
||||
if (self.delegateFlags.didScrollSelectedItemAtIndexFlag) {
|
||||
[self.delegate categoryView:self didScrollSelectedItemAtIndex:targetIndex];
|
||||
}
|
||||
}
|
||||
if (self.delegateFlags.didSelectedItemAtIndexFlag) {
|
||||
[self.delegate categoryView:self didSelectedItemAtIndex:targetIndex];
|
||||
}
|
||||
self.scrollingTargetIndex = -1;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (void)refreshSelectedCellModel:(JXCategoryBaseCellModel *)selectedCellModel unselectedCellModel:(JXCategoryBaseCellModel *)unselectedCellModel {
|
||||
selectedCellModel.selected = YES;
|
||||
unselectedCellModel.selected = NO;
|
||||
|
||||
if (self.isCellWidthZoomEnabled) {
|
||||
if (selectedCellModel.selectedType == JXCategoryCellSelectedTypeCode ||
|
||||
selectedCellModel.selectedType == JXCategoryCellSelectedTypeClick) {
|
||||
self.animator = [[JXCategoryViewAnimator alloc] init];
|
||||
self.animator.duration = self.selectedAnimationDuration;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.animator.progressCallback = ^(CGFloat percent) {
|
||||
selectedCellModel.transitionAnimating = YES;
|
||||
unselectedCellModel.transitionAnimating = YES;
|
||||
selectedCellModel.cellWidthCurrentZoomScale = [JXCategoryFactory interpolationFrom:selectedCellModel.cellWidthNormalZoomScale to:selectedCellModel.cellWidthSelectedZoomScale percent:percent];
|
||||
selectedCellModel.cellWidth = [weakSelf getCellWidthAtIndex:selectedCellModel.index] * selectedCellModel.cellWidthCurrentZoomScale;
|
||||
unselectedCellModel.cellWidthCurrentZoomScale = [JXCategoryFactory interpolationFrom:unselectedCellModel.cellWidthSelectedZoomScale to:unselectedCellModel.cellWidthNormalZoomScale percent:percent];
|
||||
unselectedCellModel.cellWidth = [weakSelf getCellWidthAtIndex:unselectedCellModel.index] * unselectedCellModel.cellWidthCurrentZoomScale;
|
||||
[weakSelf.collectionView.collectionViewLayout invalidateLayout];
|
||||
};
|
||||
self.animator.completeCallback = ^{
|
||||
selectedCellModel.transitionAnimating = NO;
|
||||
unselectedCellModel.transitionAnimating = NO;
|
||||
};
|
||||
[self.animator start];
|
||||
} else {
|
||||
selectedCellModel.cellWidthCurrentZoomScale = selectedCellModel.cellWidthSelectedZoomScale;
|
||||
selectedCellModel.cellWidth = [self getCellWidthAtIndex:selectedCellModel.index] * selectedCellModel.cellWidthCurrentZoomScale;
|
||||
unselectedCellModel.cellWidthCurrentZoomScale = unselectedCellModel.cellWidthNormalZoomScale;
|
||||
unselectedCellModel.cellWidth = [self getCellWidthAtIndex:unselectedCellModel.index] * unselectedCellModel.cellWidthCurrentZoomScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)contentOffsetOfContentScrollViewDidChanged:(CGPoint)contentOffset {
|
||||
if (self.dataSource.count == 0) {
|
||||
return;
|
||||
}
|
||||
CGFloat ratio = contentOffset.x/self.contentScrollView.bounds.size.width;
|
||||
if (ratio > self.dataSource.count - 1 || ratio < 0) {
|
||||
//超过了边界,不需要处理
|
||||
return;
|
||||
}
|
||||
if (contentOffset.x == 0 && self.selectedIndex == 0 && self.lastContentViewContentOffset.x == 0) {
|
||||
//滚动到了最左边,且已经选中了第一个,且之前的contentOffset.x为0
|
||||
return;
|
||||
}
|
||||
CGFloat maxContentOffsetX = self.contentScrollView.contentSize.width - self.contentScrollView.bounds.size.width;
|
||||
if (contentOffset.x == maxContentOffsetX && self.selectedIndex == self.dataSource.count - 1 && self.lastContentViewContentOffset.x == maxContentOffsetX) {
|
||||
//滚动到了最右边,且已经选中了最后一个,且之前的contentOffset.x为maxContentOffsetX
|
||||
return;
|
||||
}
|
||||
ratio = MAX(0, MIN(self.dataSource.count - 1, ratio));
|
||||
NSInteger baseIndex = floorf(ratio);
|
||||
CGFloat remainderRatio = ratio - baseIndex;
|
||||
|
||||
if (remainderRatio == 0) {
|
||||
//快速滑动翻页,用户一直在拖拽contentScrollView,需要更新选中状态
|
||||
//滑动一小段距离,然后放开回到原位,contentOffset同样的值会回调多次。例如在index为1的情况,滑动放开回到原位,contentOffset会多次回调CGPoint(width, 0)
|
||||
if (!(self.lastContentViewContentOffset.x == contentOffset.x && self.selectedIndex == baseIndex)) {
|
||||
[self scrollSelectItemAtIndex:baseIndex];
|
||||
}
|
||||
} else {
|
||||
self.needReloadByBecomeActive = YES;
|
||||
if (self.animator.isExecuting) {
|
||||
[self.animator invalid];
|
||||
//需要重置之前animator.progessCallback为处理完的状态
|
||||
for (JXCategoryBaseCellModel *model in self.dataSource) {
|
||||
if (model.isSelected) {
|
||||
model.cellWidthCurrentZoomScale = model.cellWidthSelectedZoomScale;
|
||||
model.cellWidth = [self getCellWidthAtIndex:model.index] * model.cellWidthCurrentZoomScale;
|
||||
}else {
|
||||
model.cellWidthCurrentZoomScale = model.cellWidthNormalZoomScale;
|
||||
model.cellWidth = [self getCellWidthAtIndex:model.index] * model.cellWidthCurrentZoomScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
//快速滑动翻页,当remainderRatio没有变成0,但是已经翻页了,需要通过下面的判断,触发选中
|
||||
if (fabs(ratio - self.selectedIndex) > 1) {
|
||||
NSInteger targetIndex = baseIndex;
|
||||
if (ratio < self.selectedIndex) {
|
||||
targetIndex = baseIndex + 1;
|
||||
}
|
||||
[self scrollSelectItemAtIndex:targetIndex];
|
||||
}
|
||||
|
||||
if (self.selectedIndex == baseIndex) {
|
||||
self.scrollingTargetIndex = baseIndex + 1;
|
||||
} else {
|
||||
self.scrollingTargetIndex = baseIndex;
|
||||
}
|
||||
|
||||
if (self.isCellWidthZoomEnabled && self.isCellWidthZoomScrollGradientEnabled) {
|
||||
JXCategoryBaseCellModel *leftCellModel = (JXCategoryBaseCellModel *)self.dataSource[baseIndex];
|
||||
JXCategoryBaseCellModel *rightCellModel = (JXCategoryBaseCellModel *)self.dataSource[baseIndex + 1];
|
||||
leftCellModel.cellWidthCurrentZoomScale = [JXCategoryFactory interpolationFrom:leftCellModel.cellWidthSelectedZoomScale to:leftCellModel.cellWidthNormalZoomScale percent:remainderRatio];
|
||||
leftCellModel.cellWidth = [self getCellWidthAtIndex:leftCellModel.index] * leftCellModel.cellWidthCurrentZoomScale;
|
||||
rightCellModel.cellWidthCurrentZoomScale = [JXCategoryFactory interpolationFrom:rightCellModel.cellWidthNormalZoomScale to:rightCellModel.cellWidthSelectedZoomScale percent:remainderRatio];
|
||||
rightCellModel.cellWidth = [self getCellWidthAtIndex:rightCellModel.index] * rightCellModel.cellWidthCurrentZoomScale;
|
||||
[self.collectionView.collectionViewLayout invalidateLayout];
|
||||
}
|
||||
|
||||
if (self.delegateFlags.scrollingFromLeftIndexToRightIndexFlag) {
|
||||
[self.delegate categoryView:self scrollingFromLeftIndex:baseIndex toRightIndex:baseIndex + 1 ratio:remainderRatio];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)preferredCellWidthAtIndex:(NSInteger)index {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (Class)preferredCellClass {
|
||||
return JXCategoryBaseCell.class;
|
||||
}
|
||||
|
||||
- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
24
Pods/JXCategoryView/Sources/Common/JXCategoryCollectionView.h
generated
Normal file
24
Pods/JXCategoryView/Sources/Common/JXCategoryCollectionView.h
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// JXCategoryCollectionView.h
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/21.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryIndicatorProtocol.h"
|
||||
@class JXCategoryCollectionView;
|
||||
|
||||
@protocol JXCategoryCollectionViewGestureDelegate <NSObject>
|
||||
@optional
|
||||
- (BOOL)categoryCollectionView:(JXCategoryCollectionView *)collectionView gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
|
||||
- (BOOL)categoryCollectionView:(JXCategoryCollectionView *)collectionView gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
|
||||
@end
|
||||
|
||||
@interface JXCategoryCollectionView : UICollectionView
|
||||
|
||||
@property (nonatomic, strong) NSArray <UIView<JXCategoryIndicatorProtocol> *> *indicators;
|
||||
@property (nonatomic, weak) id<JXCategoryCollectionViewGestureDelegate> gestureDelegate;
|
||||
|
||||
@end
|
||||
52
Pods/JXCategoryView/Sources/Common/JXCategoryCollectionView.m
generated
Normal file
52
Pods/JXCategoryView/Sources/Common/JXCategoryCollectionView.m
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// JXCategoryCollectionView.m
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/21.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryCollectionView.h"
|
||||
|
||||
@interface JXCategoryCollectionView ()<UIGestureRecognizerDelegate>
|
||||
@end
|
||||
|
||||
@implementation JXCategoryCollectionView
|
||||
|
||||
- (void)setIndicators:(NSArray<UIView<JXCategoryIndicatorProtocol> *> *)indicators {
|
||||
for (UIView *indicator in _indicators) {
|
||||
//先移除之前的indicator
|
||||
[indicator removeFromSuperview];
|
||||
}
|
||||
|
||||
_indicators = indicators;
|
||||
|
||||
for (UIView *indicator in indicators) {
|
||||
[self addSubview:indicator];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
for (UIView<JXCategoryIndicatorProtocol> *view in self.indicators) {
|
||||
[self sendSubviewToBack:view];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
|
||||
if (self.gestureDelegate && [self.gestureDelegate respondsToSelector:@selector(categoryCollectionView:gestureRecognizerShouldBegin:)]) {
|
||||
return [self.gestureDelegate categoryCollectionView:self gestureRecognizerShouldBegin:gestureRecognizer];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
|
||||
if (self.gestureDelegate && [self.gestureDelegate respondsToSelector:@selector(categoryCollectionView:gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]) {
|
||||
return [self.gestureDelegate categoryCollectionView:self gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
18
Pods/JXCategoryView/Sources/Common/JXCategoryFactory.h
generated
Normal file
18
Pods/JXCategoryView/Sources/Common/JXCategoryFactory.h
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// JXCategoryFactory.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface JXCategoryFactory : NSObject
|
||||
|
||||
+ (CGFloat)interpolationFrom:(CGFloat)from to:(CGFloat)to percent:(CGFloat)percent;
|
||||
|
||||
+ (UIColor *)interpolationColorFrom:(UIColor *)fromColor to:(UIColor *)toColor percent:(CGFloat)percent;
|
||||
|
||||
@end
|
||||
29
Pods/JXCategoryView/Sources/Common/JXCategoryFactory.m
generated
Normal file
29
Pods/JXCategoryView/Sources/Common/JXCategoryFactory.m
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// JXCategoryFactory.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryFactory.h"
|
||||
#import "UIColor+JXAdd.h"
|
||||
|
||||
@implementation JXCategoryFactory
|
||||
|
||||
+ (CGFloat)interpolationFrom:(CGFloat)from to:(CGFloat)to percent:(CGFloat)percent
|
||||
{
|
||||
percent = MAX(0, MIN(1, percent));
|
||||
return from + (to - from)*percent;
|
||||
}
|
||||
|
||||
+ (UIColor *)interpolationColorFrom:(UIColor *)fromColor to:(UIColor *)toColor percent:(CGFloat)percent
|
||||
{
|
||||
CGFloat red = [self interpolationFrom:fromColor.jx_red to:toColor.jx_red percent:percent];
|
||||
CGFloat green = [self interpolationFrom:fromColor.jx_green to:toColor.jx_green percent:percent];
|
||||
CGFloat blue = [self interpolationFrom:fromColor.jx_blue to:toColor.jx_blue percent:percent];
|
||||
CGFloat alpha = [self interpolationFrom:fromColor.jx_alpha to:toColor.jx_alpha percent:percent];
|
||||
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
|
||||
}
|
||||
|
||||
@end
|
||||
31
Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorParamsModel.h
generated
Normal file
31
Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorParamsModel.h
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// JXCategoryIndicatorParamsModel.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/12/13.
|
||||
// Copyright © 2018 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryViewDefines.h"
|
||||
|
||||
/**
|
||||
指示器不同情况处理时传递的数据模型,不同情况会对不同的属性赋值,根据不同情况的 api 说明确认。
|
||||
|
||||
FAQ: 为什么会通过 model 传递数据?
|
||||
因为指示器处理逻辑以后会扩展不同的使用场景,会新增参数,如果不通过 model 传递,就会在 api 新增参数,一旦修改 api 改的地方就特别多了,而且会影响到之前自定义实现的开发者。
|
||||
*/
|
||||
@interface JXCategoryIndicatorParamsModel : NSObject
|
||||
|
||||
@property (nonatomic, assign) NSInteger selectedIndex; // 当前选中的 index
|
||||
@property (nonatomic, assign) CGRect selectedCellFrame; // 当前选中的 cellFrame
|
||||
@property (nonatomic, assign) NSInteger leftIndex; // 正在过渡中的两个 cell,相对位置在左边的 cell 的 index
|
||||
@property (nonatomic, assign) CGRect leftCellFrame; // 正在过渡中的两个 cell,相对位置在左边的 cell 的 frame
|
||||
@property (nonatomic, assign) NSInteger rightIndex; // 正在过渡中的两个 cell,相对位置在右边的 cell 的 index
|
||||
@property (nonatomic, assign) CGRect rightCellFrame; // 正在过渡中的两个 cell,相对位置在右边的 cell 的 frame
|
||||
@property (nonatomic, assign) CGFloat percent; // 正在过渡中的两个 cell,从左到右的百分比
|
||||
@property (nonatomic, assign) NSInteger lastSelectedIndex; // 之前选中的 index
|
||||
@property (nonatomic, assign) JXCategoryCellSelectedType selectedType; //cell 被选中类型
|
||||
|
||||
@end
|
||||
13
Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorParamsModel.m
generated
Normal file
13
Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorParamsModel.m
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryIndicatorParamsModel.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/12/13.
|
||||
// Copyright © 2018 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorParamsModel.h"
|
||||
|
||||
@implementation JXCategoryIndicatorParamsModel
|
||||
|
||||
@end
|
||||
49
Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorProtocol.h
generated
Normal file
49
Pods/JXCategoryView/Sources/Common/JXCategoryIndicatorProtocol.h
generated
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// JXCategoryIndicatorProtocol.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryViewDefines.h"
|
||||
#import "JXCategoryIndicatorParamsModel.h"
|
||||
|
||||
@protocol JXCategoryIndicatorProtocol <NSObject>
|
||||
|
||||
/**
|
||||
categoryView 重置状态时调用
|
||||
|
||||
param selectedIndex 当前选中的 index
|
||||
param selectedCellFrame 当前选中的 cellFrame
|
||||
@param model 数据模型
|
||||
*/
|
||||
- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model;
|
||||
|
||||
/**
|
||||
contentScrollView在进行手势滑动时,处理指示器跟随手势变化UI逻辑;
|
||||
|
||||
param selectedIndex 当前选中的index
|
||||
param leftIndex 正在过渡中的两个cell,相对位置在左边的cell的index
|
||||
param leftCellFrame 正在过渡中的两个cell,相对位置在左边的cell的frame
|
||||
param rightIndex 正在过渡中的两个cell,相对位置在右边的cell的index
|
||||
param rightCellFrame 正在过渡中的两个cell,相对位置在右边的cell的frame
|
||||
param percent 过渡百分比
|
||||
@param model 数据模型
|
||||
*/
|
||||
- (void)jx_contentScrollViewDidScroll:(JXCategoryIndicatorParamsModel *)model;
|
||||
|
||||
/**
|
||||
选中了某一个cell
|
||||
|
||||
param lastSelectedIndex 之前选中的index
|
||||
param selectedIndex 选中的index
|
||||
param selectedCellFrame 选中的cellFrame
|
||||
param selectedType cell被选中类型
|
||||
@param model 数据模型
|
||||
*/
|
||||
- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model;
|
||||
|
||||
@end
|
||||
16
Pods/JXCategoryView/Sources/Common/JXCategoryListContainerRTLCell.h
generated
Normal file
16
Pods/JXCategoryView/Sources/Common/JXCategoryListContainerRTLCell.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// JXCategoryListContainerRTLCell.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2020/7/3.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface JXCategoryListContainerRTLCell : UICollectionViewCell
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
23
Pods/JXCategoryView/Sources/Common/JXCategoryListContainerRTLCell.m
generated
Normal file
23
Pods/JXCategoryView/Sources/Common/JXCategoryListContainerRTLCell.m
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// JXCategoryListContainerRTLCell.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2020/7/3.
|
||||
//
|
||||
|
||||
#import "JXCategoryListContainerRTLCell.h"
|
||||
#import "RTLManager.h"
|
||||
|
||||
@implementation JXCategoryListContainerRTLCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[RTLManager horizontalFlipViewIfNeeded:self];
|
||||
[RTLManager horizontalFlipViewIfNeeded:self.contentView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
122
Pods/JXCategoryView/Sources/Common/JXCategoryListContainerView.h
generated
Normal file
122
Pods/JXCategoryView/Sources/Common/JXCategoryListContainerView.h
generated
Normal file
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// JXCategoryListScrollView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/9/12.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryViewDefines.h"
|
||||
#import "JXCategoryBaseView.h"
|
||||
@class JXCategoryListContainerView;
|
||||
|
||||
/**
|
||||
列表容器视图的类型
|
||||
|
||||
- ScrollView: UIScrollView。优势:没有其他副作用。劣势:视图内存占用相对大一点。
|
||||
- CollectionView: 使用UICollectionView。优势:因为列表被添加到cell上,视图的内存占用更少,适合内存要求特别高的场景。劣势:因为cell重用机制的问题,导致列表下拉刷新视图,会因为被removeFromSuperview而被隐藏。需要参考`LoadDataListCollectionListViewController`类做特殊处理。
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, JXCategoryListContainerType) {
|
||||
JXCategoryListContainerType_ScrollView,
|
||||
JXCategoryListContainerType_CollectionView,
|
||||
};
|
||||
|
||||
@protocol JXCategoryListContentViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
如果列表是VC,就返回VC.view
|
||||
如果列表是View,就返回View自己
|
||||
|
||||
@return 返回列表视图
|
||||
*/
|
||||
- (UIView *)listView;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
可选实现,列表将要显示的时候调用
|
||||
*/
|
||||
- (void)listWillAppear;
|
||||
|
||||
/**
|
||||
可选实现,列表显示的时候调用
|
||||
*/
|
||||
- (void)listDidAppear;
|
||||
|
||||
/**
|
||||
可选实现,列表将要消失的时候调用
|
||||
*/
|
||||
- (void)listWillDisappear;
|
||||
|
||||
/**
|
||||
可选实现,列表消失的时候调用
|
||||
*/
|
||||
- (void)listDidDisappear;
|
||||
|
||||
@end
|
||||
|
||||
@protocol JXCategoryListContainerViewDelegate <NSObject>
|
||||
/**
|
||||
返回list的数量
|
||||
|
||||
@param listContainerView 列表的容器视图
|
||||
@return list的数量
|
||||
*/
|
||||
- (NSInteger)numberOfListsInlistContainerView:(JXCategoryListContainerView *)listContainerView;
|
||||
|
||||
/**
|
||||
根据index返回一个对应列表实例,需要是遵从`JXCategoryListContentViewDelegate`协议的对象。
|
||||
你可以代理方法调用的时候初始化对应列表,达到懒加载的效果。这也是默认推荐的初始化列表方法。你也可以提前创建好列表,等该代理方法回调的时候再返回也可以,达到预加载的效果。
|
||||
如果列表是用自定义UIView封装的,就让自定义UIView遵从`JXCategoryListContentViewDelegate`协议,该方法返回自定义UIView即可。
|
||||
如果列表是用自定义UIViewController封装的,就让自定义UIViewController遵从`JXCategoryListContentViewDelegate`协议,该方法返回自定义UIViewController即可。
|
||||
|
||||
@param listContainerView 列表的容器视图
|
||||
@param index 目标下标
|
||||
@return 遵从JXCategoryListContentViewDelegate协议的list实例
|
||||
*/
|
||||
- (id<JXCategoryListContentViewDelegate>)listContainerView:(JXCategoryListContainerView *)listContainerView initListForIndex:(NSInteger)index;
|
||||
|
||||
@optional
|
||||
/**
|
||||
返回自定义UIScrollView或UICollectionView的Class
|
||||
某些特殊情况需要自己处理UIScrollView内部逻辑。比如项目用了FDFullscreenPopGesture,需要处理手势相关代理。
|
||||
|
||||
@param listContainerView JXCategoryListContainerView
|
||||
@return 自定义UIScrollView实例
|
||||
*/
|
||||
- (Class)scrollViewClassInlistContainerView:(JXCategoryListContainerView *)listContainerView;
|
||||
|
||||
/**
|
||||
控制能否初始化对应index的列表。有些业务需求,需要在某些情况才允许初始化某些列表,通过通过该代理实现控制。
|
||||
*/
|
||||
- (BOOL)listContainerView:(JXCategoryListContainerView *)listContainerView canInitListAtIndex:(NSInteger)index;
|
||||
|
||||
- (void)listContainerViewDidScroll:(UIScrollView *)scrollView;
|
||||
- (void)listContainerViewWillBeginDragging:(UIScrollView *)scrollView;
|
||||
- (void)listContainerViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
|
||||
- (void)listContainerViewWillBeginDecelerating:(UIScrollView *)scrollView;
|
||||
- (void)listContainerViewDidEndDecelerating:(UIScrollView *)scrollView;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface JXCategoryListContainerView : UIView <JXCategoryViewListContainer>
|
||||
|
||||
@property (nonatomic, assign, readonly) JXCategoryListContainerType containerType;
|
||||
@property (nonatomic, strong, readonly) UIScrollView *scrollView;
|
||||
@property (nonatomic, strong, readonly) NSDictionary <NSNumber *, id<JXCategoryListContentViewDelegate>> *validListDict; //已经加载过的列表字典。key是index,value是对应的列表
|
||||
@property (nonatomic, strong) UIColor *listCellBackgroundColor; //默认:[UIColor whiteColor]
|
||||
/**
|
||||
滚动切换的时候,滚动距离超过一页的多少百分比,就触发列表的初始化。默认0.01(即列表显示了一点就触发加载)。范围0~1,开区间不包括0和1
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat initListPercent;
|
||||
@property (nonatomic, assign) BOOL bounces; //默认NO
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
|
||||
- (instancetype)initWithType:(JXCategoryListContainerType)type delegate:(id<JXCategoryListContainerViewDelegate>)delegate NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
547
Pods/JXCategoryView/Sources/Common/JXCategoryListContainerView.m
generated
Normal file
547
Pods/JXCategoryView/Sources/Common/JXCategoryListContainerView.m
generated
Normal file
@@ -0,0 +1,547 @@
|
||||
//
|
||||
// JXCategoryListContainerView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/9/12.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryListContainerView.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "RTLManager.h"
|
||||
|
||||
@interface JXCategoryListContainerViewController : UIViewController
|
||||
@property (copy) void(^viewWillAppearBlock)(void);
|
||||
@property (copy) void(^viewDidAppearBlock)(void);
|
||||
@property (copy) void(^viewWillDisappearBlock)(void);
|
||||
@property (copy) void(^viewDidDisappearBlock)(void);
|
||||
@end
|
||||
|
||||
@implementation JXCategoryListContainerViewController
|
||||
- (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 JXCategoryListContainerView () <UIScrollViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource>
|
||||
@property (nonatomic, weak) id<JXCategoryListContainerViewDelegate> delegate;
|
||||
@property (nonatomic, strong) UIScrollView *scrollView;
|
||||
@property (nonatomic, assign) NSInteger currentIndex;
|
||||
@property (nonatomic, strong) NSMutableDictionary <NSNumber *, id<JXCategoryListContentViewDelegate>> *validListDict;
|
||||
@property (nonatomic, assign) NSInteger willAppearIndex;
|
||||
@property (nonatomic, assign) NSInteger willDisappearIndex;
|
||||
@property (nonatomic, strong) UICollectionView *collectionView;
|
||||
@property (nonatomic, strong) JXCategoryListContainerViewController *containerVC;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryListContainerView
|
||||
|
||||
- (instancetype)initWithType:(JXCategoryListContainerType)type delegate:(id<JXCategoryListContainerViewDelegate>)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 = [[JXCategoryListContainerViewController 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 == JXCategoryListContainerType_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 = [[UIScrollView alloc] init];
|
||||
}
|
||||
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, *)) {
|
||||
if ([self.scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
|
||||
self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
}
|
||||
[RTLManager horizontalFlipViewIfNeeded:self.scrollView];
|
||||
[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 = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
}
|
||||
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, *)) {
|
||||
if ([self.collectionView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
|
||||
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
}
|
||||
if ([RTLManager supportRTL]) {
|
||||
self.collectionView.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
|
||||
[RTLManager horizontalFlipView:self.collectionView];
|
||||
}
|
||||
[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 == JXCategoryListContainerType_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<JXCategoryListContentViewDelegate> _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);
|
||||
}];
|
||||
CGPoint scrollViewContentOffset = self.scrollView.contentOffset;
|
||||
scrollViewContentOffset.x = self.currentIndex*self.scrollView.bounds.size.width;
|
||||
self.scrollView.contentOffset = scrollViewContentOffset;
|
||||
}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.collectionViewLayout invalidateLayout];
|
||||
self.collectionView.frame = self.bounds;
|
||||
[self.collectionView reloadData];
|
||||
CGPoint collectionViewContentOffset = self.collectionView.contentOffset;
|
||||
collectionViewContentOffset.x =
|
||||
self.collectionView.bounds.size.width*self.currentIndex;
|
||||
[self.collectionView setContentOffset:collectionViewContentOffset 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)setBounces:(BOOL)bounces {
|
||||
_bounces = bounces;
|
||||
self.scrollView.bounces = bounces;
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
UIView* listView = nil;
|
||||
id<JXCategoryListContentViewDelegate> 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。所以,缓兵之计就是用下面的方法,暂时解决问题。
|
||||
listView = [list listView];
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
listView.frame = cell.contentView.bounds;
|
||||
} else {
|
||||
listView.frame = cell.bounds;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isAdded = NO;
|
||||
for (UIView *subview in cell.contentView.subviews) {
|
||||
if( listView != subview ) {
|
||||
[subview removeFromSuperview];
|
||||
} else {
|
||||
isAdded = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if( !isAdded && listView ) {
|
||||
[cell.contentView addSubview:listView];
|
||||
}
|
||||
|
||||
// 针对 RTL 布局
|
||||
if ([UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute]
|
||||
== UIUserInterfaceLayoutDirectionRightToLeft) {
|
||||
cell.contentView.transform = CGAffineTransformMakeScale(-1, 1);
|
||||
}
|
||||
|
||||
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(listContainerViewDidEndDecelerating:)]) {
|
||||
[self.delegate listContainerViewDidEndDecelerating:scrollView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerViewWillBeginDragging:)]) {
|
||||
[self.delegate listContainerViewWillBeginDragging:scrollView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerViewDidEndDragging:willDecelerate:)]) {
|
||||
[self.delegate listContainerViewDidEndDragging:scrollView willDecelerate:decelerate];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerViewWillBeginDecelerating:)]) {
|
||||
[self.delegate listContainerViewWillBeginDecelerating:scrollView];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - JXCategoryViewListContainer
|
||||
|
||||
- (UIScrollView *)contentScrollView {
|
||||
return self.scrollView;
|
||||
}
|
||||
|
||||
- (void)setDefaultSelectedIndex:(NSInteger)index {
|
||||
self.currentIndex = index;
|
||||
}
|
||||
|
||||
- (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<JXCategoryListContentViewDelegate> list in _validListDict.allValues) {
|
||||
[[list listView] removeFromSuperview];
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
[(UIViewController *)list removeFromParentViewController];
|
||||
}
|
||||
}
|
||||
[_validListDict removeAllObjects];
|
||||
|
||||
if (self.containerType == JXCategoryListContainerType_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<JXCategoryListContentViewDelegate> 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;
|
||||
|
||||
if (self.containerType == JXCategoryListContainerType_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]];
|
||||
[RTLManager horizontalFlipViewIfNeeded:[list listView]];
|
||||
}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]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)listWillAppear:(NSInteger)index {
|
||||
if (![self checkIndexValid:index]) {
|
||||
return;
|
||||
}
|
||||
id<JXCategoryListContentViewDelegate> list = _validListDict[@(index)];
|
||||
if (list == nil) {
|
||||
//当前列表未被创建(页面初始化或通过点击触发的listWillAppear)
|
||||
BOOL canInitList = YES;
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(listContainerView:canInitListAtIndex:)]) {
|
||||
canInitList = [self.delegate listContainerView:self canInitListAtIndex:index];
|
||||
}
|
||||
if (!canInitList) {
|
||||
return;
|
||||
}
|
||||
list = [self.delegate listContainerView:self initListForIndex:index];
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
[self.containerVC addChildViewController:(UIViewController *)list];
|
||||
}
|
||||
_validListDict[@(index)] = list;
|
||||
if (self.containerType == JXCategoryListContainerType_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]];
|
||||
[RTLManager horizontalFlipViewIfNeeded:[list listView]];
|
||||
}
|
||||
}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<JXCategoryListContentViewDelegate> list = _validListDict[@(index)];
|
||||
if (list && [list respondsToSelector:@selector(listDidAppear)]) {
|
||||
[list listDidAppear];
|
||||
}
|
||||
if ([list isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *listVC = (UIViewController *)list;
|
||||
[listVC endAppearanceTransition];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)listWillDisappear:(NSInteger)index {
|
||||
if (![self checkIndexValid:index]) {
|
||||
return;
|
||||
}
|
||||
id<JXCategoryListContentViewDelegate> 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<JXCategoryListContentViewDelegate> 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
|
||||
24
Pods/JXCategoryView/Sources/Common/JXCategoryViewAnimator.h
generated
Normal file
24
Pods/JXCategoryView/Sources/Common/JXCategoryViewAnimator.h
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// JXCategoryViewAnimator.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2019/1/24.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface JXCategoryViewAnimator : NSObject
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval duration;
|
||||
@property (nonatomic, copy) void(^progressCallback)(CGFloat percent);
|
||||
@property (nonatomic, copy) void(^completeCallback)(void);
|
||||
@property (readonly, getter=isExecuting) BOOL executing;
|
||||
|
||||
- (void)start;
|
||||
- (void)stop;
|
||||
- (void)invalid;
|
||||
|
||||
@end
|
||||
|
||||
75
Pods/JXCategoryView/Sources/Common/JXCategoryViewAnimator.m
generated
Normal file
75
Pods/JXCategoryView/Sources/Common/JXCategoryViewAnimator.m
generated
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// JXCategoryViewAnimator.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2019/1/24.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryViewAnimator.h"
|
||||
|
||||
@interface JXCategoryViewAnimator ()
|
||||
@property (nonatomic, strong) CADisplayLink *displayLink;
|
||||
@property (nonatomic, assign) CFTimeInterval firstTimestamp;
|
||||
@property (readwrite, getter=isExecuting) BOOL executing;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryViewAnimator
|
||||
|
||||
#pragma mark - Initialize
|
||||
|
||||
- (void)dealloc {
|
||||
self.progressCallback = nil;
|
||||
self.completeCallback = nil;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_executing = NO;
|
||||
_duration = 0.25;
|
||||
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(processDisplayLink:)];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (void)start {
|
||||
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||
self.executing = YES;
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
!self.progressCallback ?: self.progressCallback(1);
|
||||
[self.displayLink invalidate];
|
||||
!self.completeCallback ?: self.completeCallback();
|
||||
self.executing = NO;
|
||||
}
|
||||
|
||||
- (void)invalid {
|
||||
[self.displayLink invalidate];
|
||||
!self.completeCallback ?: self.completeCallback();
|
||||
self.executing = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)processDisplayLink:(CADisplayLink *)sender {
|
||||
if (self.firstTimestamp == 0) {
|
||||
self.firstTimestamp = sender.timestamp;
|
||||
return;
|
||||
}
|
||||
CGFloat percent = (sender.timestamp - self.firstTimestamp)/self.duration;
|
||||
if (percent >= 1) {
|
||||
!self.progressCallback ?: self.progressCallback(percent);
|
||||
[self.displayLink invalidate];
|
||||
!self.completeCallback ?: self.completeCallback();
|
||||
self.executing = NO;
|
||||
}else {
|
||||
!self.progressCallback ?: self.progressCallback(percent);
|
||||
self.executing = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
43
Pods/JXCategoryView/Sources/Common/JXCategoryViewDefines.h
generated
Normal file
43
Pods/JXCategoryView/Sources/Common/JXCategoryViewDefines.h
generated
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// JXCategoryViewDefines.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
static const CGFloat JXCategoryViewAutomaticDimension = -1;
|
||||
|
||||
typedef void(^JXCategoryCellSelectedAnimationBlock)(CGFloat percent);
|
||||
|
||||
// 指示器的位置
|
||||
typedef NS_ENUM(NSUInteger, JXCategoryComponentPosition) {
|
||||
JXCategoryComponentPosition_Bottom,
|
||||
JXCategoryComponentPosition_Top
|
||||
};
|
||||
|
||||
// cell 被选中的类型
|
||||
typedef NS_ENUM(NSUInteger, JXCategoryCellSelectedType) {
|
||||
JXCategoryCellSelectedTypeUnknown, // 未知,不是选中(cellForRow方法里面、两个cell过渡时)
|
||||
JXCategoryCellSelectedTypeClick, // 点击选中
|
||||
JXCategoryCellSelectedTypeCode, // 调用方法 selectItemAtIndex: 选中
|
||||
JXCategoryCellSelectedTypeScroll // 通过滚动到某个 cell 选中
|
||||
};
|
||||
|
||||
// cell 标题锚点位置
|
||||
typedef NS_ENUM(NSUInteger, JXCategoryTitleLabelAnchorPointStyle) {
|
||||
JXCategoryTitleLabelAnchorPointStyleCenter,
|
||||
JXCategoryTitleLabelAnchorPointStyleTop,
|
||||
JXCategoryTitleLabelAnchorPointStyleBottom
|
||||
};
|
||||
|
||||
// 指示器滚动样式
|
||||
typedef NS_ENUM(NSUInteger, JXCategoryIndicatorScrollStyle) {
|
||||
JXCategoryIndicatorScrollStyleSimple, // 简单滚动,即从当前位置过渡到目标位置
|
||||
JXCategoryIndicatorScrollStyleSameAsUserScroll // 和用户左右滚动列表时的效果一样
|
||||
};
|
||||
|
||||
#define JXCategoryViewDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead)
|
||||
18
Pods/JXCategoryView/Sources/Common/UIColor+JXAdd.h
generated
Normal file
18
Pods/JXCategoryView/Sources/Common/UIColor+JXAdd.h
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// UIColor+JXAdd.h
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/21.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIColor (JXAdd)
|
||||
|
||||
@property (nonatomic, assign, readonly) CGFloat jx_red;
|
||||
@property (nonatomic, assign, readonly) CGFloat jx_green;
|
||||
@property (nonatomic, assign, readonly) CGFloat jx_blue;
|
||||
@property (nonatomic, assign, readonly) CGFloat jx_alpha;
|
||||
|
||||
@end
|
||||
35
Pods/JXCategoryView/Sources/Common/UIColor+JXAdd.m
generated
Normal file
35
Pods/JXCategoryView/Sources/Common/UIColor+JXAdd.m
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// UIColor+JXAdd.m
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/21.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIColor+JXAdd.h"
|
||||
|
||||
@implementation UIColor (JXAdd)
|
||||
|
||||
- (CGFloat)jx_red {
|
||||
CGFloat r = 0, g, b, a;
|
||||
[self getRed:&r green:&g blue:&b alpha:&a];
|
||||
return r;
|
||||
}
|
||||
|
||||
- (CGFloat)jx_green {
|
||||
CGFloat r, g = 0, b, a;
|
||||
[self getRed:&r green:&g blue:&b alpha:&a];
|
||||
return g;
|
||||
}
|
||||
|
||||
- (CGFloat)jx_blue {
|
||||
CGFloat r, g, b = 0, a;
|
||||
[self getRed:&r green:&g blue:&b alpha:&a];
|
||||
return b;
|
||||
}
|
||||
|
||||
- (CGFloat)jx_alpha {
|
||||
return CGColorGetAlpha(self.CGColor);
|
||||
}
|
||||
|
||||
@end
|
||||
13
Pods/JXCategoryView/Sources/Dot/JXCategoryDotCell.h
generated
Normal file
13
Pods/JXCategoryView/Sources/Dot/JXCategoryDotCell.h
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryDotCell.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCell.h"
|
||||
|
||||
@interface JXCategoryDotCell : JXCategoryTitleCell
|
||||
|
||||
@end
|
||||
64
Pods/JXCategoryView/Sources/Dot/JXCategoryDotCell.m
generated
Normal file
64
Pods/JXCategoryView/Sources/Dot/JXCategoryDotCell.m
generated
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// JXCategoryDotCell.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryDotCell.h"
|
||||
#import "JXCategoryDotCellModel.h"
|
||||
|
||||
@interface JXCategoryDotCell ()
|
||||
@property (nonatomic, strong) UIView *dot;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryDotCell
|
||||
|
||||
- (void)initializeViews {
|
||||
[super initializeViews];
|
||||
|
||||
_dot = [[UIView alloc] init];
|
||||
[self.contentView addSubview:self.dot];
|
||||
self.dot.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
}
|
||||
|
||||
- (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
|
||||
[super reloadData:cellModel];
|
||||
|
||||
JXCategoryDotCellModel *myCellModel = (JXCategoryDotCellModel *)cellModel;
|
||||
self.dot.hidden = !myCellModel.dotHidden;
|
||||
self.dot.backgroundColor = myCellModel.dotColor;
|
||||
self.dot.layer.cornerRadius = myCellModel.dotCornerRadius;
|
||||
[NSLayoutConstraint deactivateConstraints:self.dot.constraints];
|
||||
[self.dot.widthAnchor constraintEqualToConstant:myCellModel.dotSize.width].active = YES;
|
||||
[self.dot.heightAnchor constraintEqualToConstant:myCellModel.dotSize.height].active = YES;
|
||||
switch (myCellModel.relativePosition) {
|
||||
case JXCategoryDotRelativePosition_TopLeft:
|
||||
{
|
||||
[self.dot.centerXAnchor constraintEqualToAnchor:self.titleLabel.leadingAnchor constant:myCellModel.dotOffset.x].active = YES;
|
||||
[self.dot.centerYAnchor constraintEqualToAnchor:self.titleLabel.topAnchor constant:myCellModel.dotOffset.y].active = YES;
|
||||
}
|
||||
break;
|
||||
case JXCategoryDotRelativePosition_TopRight:
|
||||
{
|
||||
[self.dot.centerXAnchor constraintEqualToAnchor:self.titleLabel.trailingAnchor constant:myCellModel.dotOffset.x].active = YES;
|
||||
[self.dot.centerYAnchor constraintEqualToAnchor:self.titleLabel.topAnchor constant:myCellModel.dotOffset.y].active = YES;
|
||||
}
|
||||
break;
|
||||
case JXCategoryDotRelativePosition_BottomLeft:
|
||||
{
|
||||
[self.dot.centerXAnchor constraintEqualToAnchor:self.titleLabel.leadingAnchor constant:myCellModel.dotOffset.x].active = YES;
|
||||
[self.dot.centerYAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:myCellModel.dotOffset.y].active = YES;
|
||||
}
|
||||
break;
|
||||
case JXCategoryDotRelativePosition_BottomRight:
|
||||
{
|
||||
[self.dot.centerXAnchor constraintEqualToAnchor:self.titleLabel.trailingAnchor constant:myCellModel.dotOffset.x].active = YES;
|
||||
[self.dot.centerYAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:myCellModel.dotOffset.y].active = YES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
27
Pods/JXCategoryView/Sources/Dot/JXCategoryDotCellModel.h
generated
Normal file
27
Pods/JXCategoryView/Sources/Dot/JXCategoryDotCellModel.h
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// JXCategoryDotCellModel.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCellModel.h"
|
||||
|
||||
typedef NS_ENUM(NSUInteger, JXCategoryDotRelativePosition) {
|
||||
JXCategoryDotRelativePosition_TopLeft = 0,
|
||||
JXCategoryDotRelativePosition_TopRight,
|
||||
JXCategoryDotRelativePosition_BottomLeft,
|
||||
JXCategoryDotRelativePosition_BottomRight,
|
||||
};
|
||||
|
||||
@interface JXCategoryDotCellModel : JXCategoryTitleCellModel
|
||||
|
||||
@property (nonatomic, assign) BOOL dotHidden;
|
||||
@property (nonatomic, assign) JXCategoryDotRelativePosition relativePosition;
|
||||
@property (nonatomic, assign) CGSize dotSize;
|
||||
@property (nonatomic, assign) CGFloat dotCornerRadius;
|
||||
@property (nonatomic, strong) UIColor *dotColor;
|
||||
@property (nonatomic, assign) CGPoint dotOffset;
|
||||
|
||||
@end
|
||||
13
Pods/JXCategoryView/Sources/Dot/JXCategoryDotCellModel.m
generated
Normal file
13
Pods/JXCategoryView/Sources/Dot/JXCategoryDotCellModel.m
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryDotCellModel.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryDotCellModel.h"
|
||||
|
||||
@implementation JXCategoryDotCellModel
|
||||
|
||||
@end
|
||||
30
Pods/JXCategoryView/Sources/Dot/JXCategoryDotView.h
generated
Normal file
30
Pods/JXCategoryView/Sources/Dot/JXCategoryDotView.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// JXCategoryDotView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleView.h"
|
||||
#import "JXCategoryDotCell.h"
|
||||
#import "JXCategoryDotCellModel.h"
|
||||
|
||||
@interface JXCategoryDotView : JXCategoryTitleView
|
||||
|
||||
//相对于titleLabel的位置,默认:JXCategoryDotRelativePosition_TopRight
|
||||
@property (nonatomic, assign) JXCategoryDotRelativePosition relativePosition;
|
||||
//@[@(布尔值)]数组,控制红点是否显示
|
||||
@property (nonatomic, strong) NSArray <NSNumber *> *dotStates;
|
||||
//红点的尺寸。默认:CGSizeMake(10, 10)
|
||||
@property (nonatomic, assign) CGSize dotSize;
|
||||
//红点的圆角值。默认:JXCategoryViewAutomaticDimension(self.dotSize.height/2)
|
||||
@property (nonatomic, assign) CGFloat dotCornerRadius;
|
||||
//红点的颜色。默认:[UIColor redColor]
|
||||
@property (nonatomic, strong) UIColor *dotColor;
|
||||
/**
|
||||
红点 x,y方向的偏移 (+值:水平方向向右,竖直方向向下)
|
||||
*/
|
||||
@property (nonatomic, assign) CGPoint dotOffset;
|
||||
|
||||
@end
|
||||
52
Pods/JXCategoryView/Sources/Dot/JXCategoryDotView.m
generated
Normal file
52
Pods/JXCategoryView/Sources/Dot/JXCategoryDotView.m
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// JXCategoryDotView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryDotView.h"
|
||||
|
||||
@implementation JXCategoryDotView
|
||||
|
||||
- (void)initializeData {
|
||||
[super initializeData];
|
||||
|
||||
_relativePosition = JXCategoryDotRelativePosition_TopRight;
|
||||
_dotSize = CGSizeMake(10, 10);
|
||||
_dotCornerRadius = JXCategoryViewAutomaticDimension;
|
||||
_dotColor = [UIColor redColor];
|
||||
_dotOffset = CGPointZero;
|
||||
}
|
||||
|
||||
- (Class)preferredCellClass {
|
||||
return [JXCategoryDotCell class];
|
||||
}
|
||||
|
||||
- (void)refreshDataSource {
|
||||
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:self.titles.count];
|
||||
for (int i = 0; i < self.titles.count; i++) {
|
||||
JXCategoryDotCellModel *cellModel = [[JXCategoryDotCellModel alloc] init];
|
||||
[tempArray addObject:cellModel];
|
||||
}
|
||||
self.dataSource = [NSArray arrayWithArray:tempArray];
|
||||
}
|
||||
|
||||
- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index {
|
||||
[super refreshCellModel:cellModel index:index];
|
||||
|
||||
JXCategoryDotCellModel *myCellModel = (JXCategoryDotCellModel *)cellModel;
|
||||
myCellModel.dotHidden = [self.dotStates[index] boolValue];
|
||||
myCellModel.relativePosition = self.relativePosition;
|
||||
myCellModel.dotSize = self.dotSize;
|
||||
myCellModel.dotColor = self.dotColor;
|
||||
myCellModel.dotOffset = self.dotOffset;
|
||||
if (self.dotCornerRadius == JXCategoryViewAutomaticDimension) {
|
||||
myCellModel.dotCornerRadius = self.dotSize.height/2;
|
||||
}else {
|
||||
myCellModel.dotCornerRadius = self.dotCornerRadius;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
15
Pods/JXCategoryView/Sources/Image/JXCategoryImageCell.h
generated
Normal file
15
Pods/JXCategoryView/Sources/Image/JXCategoryImageCell.h
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// JXCategoryImageCell.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorCell.h"
|
||||
|
||||
@interface JXCategoryImageCell : JXCategoryIndicatorCell
|
||||
|
||||
@property (nonatomic, strong) UIImageView *imageView;
|
||||
|
||||
@end
|
||||
97
Pods/JXCategoryView/Sources/Image/JXCategoryImageCell.m
generated
Normal file
97
Pods/JXCategoryView/Sources/Image/JXCategoryImageCell.m
generated
Normal file
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// JXCategoryImageCell.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryImageCell.h"
|
||||
#import "JXCategoryImageCellModel.h"
|
||||
|
||||
@interface JXCategoryImageCell()
|
||||
@property (nonatomic, strong) id currentImageInfo;
|
||||
@property (nonatomic, strong) NSString *currentImageName;
|
||||
@property (nonatomic, strong) NSURL *currentImageURL;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryImageCell
|
||||
|
||||
- (void)prepareForReuse {
|
||||
[super prepareForReuse];
|
||||
|
||||
self.currentImageInfo = nil;
|
||||
self.currentImageName = nil;
|
||||
self.currentImageURL = nil;
|
||||
}
|
||||
|
||||
- (void)initializeViews {
|
||||
[super initializeViews];
|
||||
|
||||
_imageView = [[UIImageView alloc] init];
|
||||
_imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
[self.contentView addSubview:_imageView];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
JXCategoryImageCellModel *myCellModel = (JXCategoryImageCellModel *)self.cellModel;
|
||||
self.imageView.bounds = CGRectMake(0, 0, myCellModel.imageSize.width, myCellModel.imageSize.height);
|
||||
self.imageView.center = self.contentView.center;
|
||||
if (myCellModel.imageCornerRadius && (myCellModel.imageCornerRadius != 0)) {
|
||||
self.imageView.layer.cornerRadius = myCellModel.imageCornerRadius;
|
||||
self.imageView.layer.masksToBounds = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
|
||||
[super reloadData:cellModel];
|
||||
|
||||
JXCategoryImageCellModel *myCellModel = (JXCategoryImageCellModel *)cellModel;
|
||||
//因为`- (void)reloadData:(JXCategoryBaseCellModel *)cellModel`方法会回调多次,尤其是左右滚动的时候会调用无数次,如果每次都触发图片加载,会非常消耗性能。所以只会在图片发生了变化的时候,才进行图片加载。
|
||||
if (myCellModel.loadImageBlock != nil) {
|
||||
id currentImageInfo = myCellModel.imageInfo;
|
||||
if (myCellModel.isSelected) {
|
||||
currentImageInfo = myCellModel.selectedImageInfo;
|
||||
}
|
||||
if (currentImageInfo && ![currentImageInfo isEqual:self.currentImageInfo]) {
|
||||
self.currentImageInfo = currentImageInfo;
|
||||
if (myCellModel.loadImageBlock) {
|
||||
myCellModel.loadImageBlock(self.imageView, currentImageInfo);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NSString *currentImageName;
|
||||
NSURL *currentImageURL;
|
||||
if (myCellModel.imageName) {
|
||||
currentImageName = myCellModel.imageName;
|
||||
} else if (myCellModel.imageURL) {
|
||||
currentImageURL = myCellModel.imageURL;
|
||||
}
|
||||
if (myCellModel.isSelected) {
|
||||
if (myCellModel.selectedImageName) {
|
||||
currentImageName = myCellModel.selectedImageName;
|
||||
} else if (myCellModel.selectedImageURL) {
|
||||
currentImageURL = myCellModel.selectedImageURL;
|
||||
}
|
||||
}
|
||||
if (currentImageName && ![currentImageName isEqualToString:self.currentImageName]) {
|
||||
self.currentImageName = currentImageName;
|
||||
self.imageView.image = [UIImage imageNamed:currentImageName];
|
||||
} else if (currentImageURL && ![currentImageURL.absoluteString isEqualToString:self.currentImageURL.absoluteString]) {
|
||||
self.currentImageURL = currentImageURL;
|
||||
if (myCellModel.loadImageCallback) {
|
||||
myCellModel.loadImageCallback(self.imageView, currentImageURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (myCellModel.isImageZoomEnabled) {
|
||||
self.imageView.transform = CGAffineTransformMakeScale(myCellModel.imageZoomScale, myCellModel.imageZoomScale);
|
||||
}else {
|
||||
self.imageView.transform = CGAffineTransformIdentity;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
32
Pods/JXCategoryView/Sources/Image/JXCategoryImageCellModel.h
generated
Normal file
32
Pods/JXCategoryView/Sources/Image/JXCategoryImageCellModel.h
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// JXCategoryImageCellModel.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorCellModel.h"
|
||||
|
||||
@interface JXCategoryImageCellModel : JXCategoryIndicatorCellModel
|
||||
|
||||
@property (nonatomic, strong) id imageInfo;
|
||||
@property (nonatomic, strong) id selectedImageInfo;
|
||||
@property (nonatomic, copy) void(^loadImageBlock)(UIImageView *imageView, id info);
|
||||
|
||||
@property (nonatomic, assign) CGSize imageSize;
|
||||
|
||||
@property (nonatomic, assign) CGFloat imageCornerRadius;
|
||||
|
||||
@property (nonatomic, assign, getter=isImageZoomEnabled) BOOL imageZoomEnabled;
|
||||
|
||||
@property (nonatomic, assign) CGFloat imageZoomScale;
|
||||
|
||||
/// 以下属性将会被弃用
|
||||
@property (nonatomic, copy) NSString *imageName; //加载bundle内的图片
|
||||
@property (nonatomic, strong) NSURL *imageURL; //图片URL
|
||||
@property (nonatomic, copy) NSString *selectedImageName;
|
||||
@property (nonatomic, strong) NSURL *selectedImageURL;
|
||||
@property (nonatomic, copy) void(^loadImageCallback)(UIImageView *imageView, NSURL *imageURL);
|
||||
|
||||
@end
|
||||
13
Pods/JXCategoryView/Sources/Image/JXCategoryImageCellModel.m
generated
Normal file
13
Pods/JXCategoryView/Sources/Image/JXCategoryImageCellModel.m
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryImageCellModel.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryImageCellModel.h"
|
||||
|
||||
@implementation JXCategoryImageCellModel
|
||||
|
||||
@end
|
||||
32
Pods/JXCategoryView/Sources/Image/JXCategoryImageView.h
generated
Normal file
32
Pods/JXCategoryView/Sources/Image/JXCategoryImageView.h
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// JXCategoryImageView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorView.h"
|
||||
#import "JXCategoryImageCell.h"
|
||||
#import "JXCategoryImageCellModel.h"
|
||||
|
||||
@interface JXCategoryImageView : JXCategoryIndicatorView
|
||||
|
||||
//imageInfo数组可以传入imageName字符串或者image的URL地址等,然后会通过loadImageBlock透传回来,把imageView对于图片的加载过程完全交给使用者决定。
|
||||
@property (nonatomic, strong) NSArray <id>*imageInfoArray;
|
||||
@property (nonatomic, strong) NSArray <id>*selectedImageInfoArray;
|
||||
@property (nonatomic, copy) void(^loadImageBlock)(UIImageView *imageView, id info);
|
||||
|
||||
@property (nonatomic, assign) CGSize imageSize; //默认值为 CGSizeMake(20, 20)
|
||||
@property (nonatomic, assign) CGFloat imageCornerRadius; //图片圆角
|
||||
@property (nonatomic, assign, getter=isImageZoomEnabled) BOOL imageZoomEnabled; //默认值为 NO
|
||||
@property (nonatomic, assign) CGFloat imageZoomScale; //默认值为 1.2,imageZoomEnabled 为 YES 时才生效
|
||||
|
||||
//下面的属性将会被弃用,请使用`imageInfoArray`、`selectedImageInfoArray`、`loadImageBlock`属性完成需求。
|
||||
@property (nonatomic, strong) NSArray <NSString *>*imageNames;
|
||||
@property (nonatomic, strong) NSArray <NSURL *>*imageURLs;
|
||||
@property (nonatomic, strong) NSArray <NSString *>*selectedImageNames;
|
||||
@property (nonatomic, strong) NSArray <NSURL *>*selectedImageURLs;
|
||||
@property (nonatomic, copy) void(^loadImageCallback)(UIImageView *imageView, NSURL *imageURL); //使用imageURL从远端下载图片进行加载,建议使用SDWebImage等第三方库进行下载。
|
||||
|
||||
@end
|
||||
104
Pods/JXCategoryView/Sources/Image/JXCategoryImageView.m
generated
Normal file
104
Pods/JXCategoryView/Sources/Image/JXCategoryImageView.m
generated
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// JXCategoryImageView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/20.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryImageView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@implementation JXCategoryImageView
|
||||
|
||||
- (void)dealloc {
|
||||
self.loadImageBlock = nil;
|
||||
self.loadImageCallback = nil;
|
||||
}
|
||||
|
||||
- (void)initializeData {
|
||||
[super initializeData];
|
||||
|
||||
_imageSize = CGSizeMake(20, 20);
|
||||
_imageZoomEnabled = NO;
|
||||
_imageZoomScale = 1.2;
|
||||
_imageCornerRadius = 0;
|
||||
}
|
||||
|
||||
- (Class)preferredCellClass {
|
||||
return [JXCategoryImageCell class];
|
||||
}
|
||||
|
||||
- (void)refreshDataSource {
|
||||
NSUInteger count = 0;
|
||||
if (self.imageInfoArray.count > 0) {
|
||||
count = self.imageInfoArray.count;
|
||||
}else if (self.imageNames.count > 0) {
|
||||
count = self.imageNames.count;
|
||||
}else {
|
||||
count = self.imageURLs.count;
|
||||
}
|
||||
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
JXCategoryImageCellModel *cellModel = [[JXCategoryImageCellModel alloc] init];
|
||||
[tempArray addObject:cellModel];
|
||||
}
|
||||
self.dataSource = [NSArray arrayWithArray:tempArray];
|
||||
}
|
||||
|
||||
- (void)refreshSelectedCellModel:(JXCategoryBaseCellModel *)selectedCellModel unselectedCellModel:(JXCategoryBaseCellModel *)unselectedCellModel {
|
||||
[super refreshSelectedCellModel:selectedCellModel unselectedCellModel:unselectedCellModel];
|
||||
|
||||
JXCategoryImageCellModel *myUnselectedCellModel = (JXCategoryImageCellModel *)unselectedCellModel;
|
||||
myUnselectedCellModel.imageZoomScale = 1.0;
|
||||
|
||||
JXCategoryImageCellModel *myselectedCellModel = (JXCategoryImageCellModel *)selectedCellModel;
|
||||
myselectedCellModel.imageZoomScale = self.imageZoomScale;
|
||||
}
|
||||
|
||||
- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index {
|
||||
[super refreshCellModel:cellModel index:index];
|
||||
|
||||
JXCategoryImageCellModel *myCellModel = (JXCategoryImageCellModel *)cellModel;
|
||||
myCellModel.loadImageBlock = self.loadImageBlock;
|
||||
myCellModel.loadImageCallback = self.loadImageCallback;
|
||||
myCellModel.imageSize = self.imageSize;
|
||||
myCellModel.imageCornerRadius = self.imageCornerRadius;
|
||||
if (self.imageInfoArray && self.imageInfoArray.count != 0) {
|
||||
myCellModel.imageInfo = self.imageInfoArray[index];
|
||||
}else if (self.imageNames && self.imageNames.count != 0) {
|
||||
myCellModel.imageName = self.imageNames[index];
|
||||
}else if (self.imageURLs && self.imageURLs.count != 0) {
|
||||
myCellModel.imageURL = self.imageURLs[index];
|
||||
}
|
||||
if (self.selectedImageInfoArray && self.selectedImageInfoArray.count != 0) {
|
||||
myCellModel.selectedImageInfo = self.selectedImageInfoArray[index];
|
||||
}else if (self.selectedImageNames && self.selectedImageNames.count != 0) {
|
||||
myCellModel.selectedImageName = self.selectedImageNames[index];
|
||||
}else if (self.selectedImageURLs && self.selectedImageURLs.count != 0) {
|
||||
myCellModel.selectedImageURL = self.selectedImageURLs[index];
|
||||
}
|
||||
myCellModel.imageZoomEnabled = self.imageZoomEnabled;
|
||||
myCellModel.imageZoomScale = ((index == self.selectedIndex) ? self.imageZoomScale : 1.0);
|
||||
}
|
||||
|
||||
- (void)refreshLeftCellModel:(JXCategoryBaseCellModel *)leftCellModel rightCellModel:(JXCategoryBaseCellModel *)rightCellModel ratio:(CGFloat)ratio {
|
||||
[super refreshLeftCellModel:leftCellModel rightCellModel:rightCellModel ratio:ratio];
|
||||
|
||||
JXCategoryImageCellModel *leftModel = (JXCategoryImageCellModel *)leftCellModel;
|
||||
JXCategoryImageCellModel *rightModel = (JXCategoryImageCellModel *)rightCellModel;
|
||||
|
||||
if (self.isImageZoomEnabled) {
|
||||
leftModel.imageZoomScale = [JXCategoryFactory interpolationFrom:self.imageZoomScale to:1.0 percent:ratio];
|
||||
rightModel.imageZoomScale = [JXCategoryFactory interpolationFrom:1.0 to:self.imageZoomScale percent:ratio];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)preferredCellWidthAtIndex:(NSInteger)index {
|
||||
if (self.cellWidth == JXCategoryViewAutomaticDimension) {
|
||||
return self.imageSize.width;
|
||||
}
|
||||
return self.cellWidth;
|
||||
}
|
||||
|
||||
@end
|
||||
14
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorBackgroundView.h
generated
Normal file
14
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorBackgroundView.h
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// JXCategoryIndicatorBackgroundView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorComponentView.h"
|
||||
|
||||
/// BackgroundView 样式的指示器
|
||||
@interface JXCategoryIndicatorBackgroundView : JXCategoryIndicatorComponentView
|
||||
|
||||
@end
|
||||
101
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorBackgroundView.m
generated
Normal file
101
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorBackgroundView.m
generated
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// JXCategoryIndicatorBackgroundView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorBackgroundView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@implementation JXCategoryIndicatorBackgroundView
|
||||
|
||||
#pragma mark - Initialize
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self configureDefaulteValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self configureDefaulteValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureDefaulteValue {
|
||||
self.indicatorWidth = JXCategoryViewAutomaticDimension;
|
||||
self.indicatorHeight = JXCategoryViewAutomaticDimension;
|
||||
self.indicatorCornerRadius = JXCategoryViewAutomaticDimension;
|
||||
self.indicatorColor = [UIColor lightGrayColor];
|
||||
self.indicatorWidthIncrement = 10;
|
||||
}
|
||||
|
||||
#pragma mark - JXCategoryIndicatorProtocol
|
||||
|
||||
- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model {
|
||||
self.layer.cornerRadius = [self indicatorCornerRadiusValue:model.selectedCellFrame];
|
||||
self.backgroundColor = self.indicatorColor;
|
||||
|
||||
CGFloat width = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
CGFloat height = [self indicatorHeightValue:model.selectedCellFrame];
|
||||
CGFloat x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - width)/2;
|
||||
CGFloat y = (model.selectedCellFrame.size.height - height)/2 - self.verticalMargin;
|
||||
self.frame = CGRectMake(x, y, width, height);
|
||||
}
|
||||
|
||||
- (void)jx_contentScrollViewDidScroll:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGRect rightCellFrame = model.rightCellFrame;
|
||||
CGRect leftCellFrame = model.leftCellFrame;
|
||||
CGFloat percent = model.percent;
|
||||
CGFloat targetX = 0;
|
||||
CGFloat targetWidth = [self indicatorWidthValue:leftCellFrame];
|
||||
|
||||
if (percent == 0) {
|
||||
targetX = leftCellFrame.origin.x + (leftCellFrame.size.width - targetWidth)/2.0;
|
||||
}else {
|
||||
CGFloat leftWidth = targetWidth;
|
||||
CGFloat rightWidth = [self indicatorWidthValue:rightCellFrame];
|
||||
|
||||
CGFloat leftX = leftCellFrame.origin.x + (leftCellFrame.size.width - leftWidth)/2;
|
||||
CGFloat rightX = rightCellFrame.origin.x + (rightCellFrame.size.width - rightWidth)/2;
|
||||
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:rightX percent:percent];
|
||||
|
||||
if (self.indicatorWidth == JXCategoryViewAutomaticDimension) {
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:leftWidth to:rightWidth percent:percent];
|
||||
}
|
||||
}
|
||||
|
||||
//允许变动frame的情况:1、允许滚动;2、不允许滚动,但是已经通过手势滚动切换一页内容了;
|
||||
if (self.isScrollEnabled == YES || (self.isScrollEnabled == NO && percent == 0)) {
|
||||
CGRect toFrame = self.frame;
|
||||
toFrame.origin.x = targetX;
|
||||
toFrame.size.width = targetWidth;
|
||||
self.frame = toFrame;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGFloat width = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
CGRect toFrame = self.frame;
|
||||
toFrame.origin.x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - width)/2;
|
||||
toFrame.size.width = width;
|
||||
|
||||
if (self.isScrollEnabled) {
|
||||
[UIView animateWithDuration:self.scrollAnimationDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
|
||||
self.frame = toFrame;
|
||||
} completion:^(BOOL finished) {
|
||||
}];
|
||||
}else {
|
||||
self.frame = toFrame;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
17
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorBallView.h
generated
Normal file
17
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorBallView.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// JXCategoryIndicatorBallView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/21.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorComponentView.h"
|
||||
|
||||
/// QQ 小红点样式的指示器
|
||||
@interface JXCategoryIndicatorBallView : JXCategoryIndicatorComponentView
|
||||
|
||||
// 球沿的 X 轴方向上的偏移量。默认值为 20
|
||||
@property (nonatomic, assign) CGFloat ballScrollOffsetX;
|
||||
|
||||
@end
|
||||
199
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorBallView.m
generated
Normal file
199
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorBallView.m
generated
Normal file
@@ -0,0 +1,199 @@
|
||||
//
|
||||
// JXCategoryIndicatorBallView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/21.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorBallView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@interface JXCategoryIndicatorBallView ()
|
||||
@property (nonatomic, strong) UIView *smallBall;
|
||||
@property (nonatomic, strong) UIView *bigBall;
|
||||
@property (nonatomic, strong) CAShapeLayer *shapeLayer;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryIndicatorBallView
|
||||
|
||||
#pragma mark - Initialize
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self configureIndicatorBall];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self configureIndicatorBall];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureIndicatorBall {
|
||||
self.indicatorWidth = 15;
|
||||
self.indicatorHeight = 15;
|
||||
_ballScrollOffsetX = 20;
|
||||
|
||||
_smallBall = [[UIView alloc] init];
|
||||
[self addSubview:self.smallBall];
|
||||
|
||||
_bigBall = [[UIView alloc] init];
|
||||
[self addSubview:self.bigBall];
|
||||
|
||||
_shapeLayer = [CAShapeLayer layer];
|
||||
[self.layer addSublayer:self.shapeLayer];
|
||||
}
|
||||
|
||||
#pragma mark - JXCategoryIndicatorProtocol
|
||||
|
||||
- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGFloat ballWidth = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
CGFloat ballHeight = [self indicatorHeightValue:model.selectedCellFrame];
|
||||
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
self.shapeLayer.fillColor = self.indicatorColor.CGColor;
|
||||
[CATransaction commit];
|
||||
|
||||
self.smallBall.backgroundColor = self.indicatorColor;
|
||||
self.smallBall.layer.cornerRadius = ballHeight/2;
|
||||
self.bigBall.backgroundColor = self.indicatorColor;
|
||||
self.bigBall.layer.cornerRadius = ballHeight/2;
|
||||
|
||||
CGFloat x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - ballWidth)/2;
|
||||
CGFloat y = self.superview.bounds.size.height - ballHeight - self.verticalMargin;
|
||||
if (self.componentPosition == JXCategoryComponentPosition_Top) {
|
||||
y = self.verticalMargin;
|
||||
}
|
||||
self.smallBall.frame = CGRectMake(x, y, ballWidth, ballHeight);
|
||||
self.bigBall.frame = CGRectMake(x, y, ballWidth, ballHeight);
|
||||
}
|
||||
|
||||
- (void)jx_contentScrollViewDidScroll:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGFloat ballWidth = [self indicatorWidthValue:model.leftCellFrame];
|
||||
CGFloat ballHeight = [self indicatorHeightValue:model.leftCellFrame];
|
||||
CGRect rightCellFrame = model.rightCellFrame;
|
||||
CGRect leftCellFrame = model.leftCellFrame;
|
||||
CGFloat percent = model.percent;
|
||||
CGFloat targetXOfBigBall = 0;
|
||||
CGFloat targetXOfSmallBall = leftCellFrame.origin.x + (leftCellFrame.size.width - ballWidth)/2;
|
||||
CGFloat targetWidthOfSmallBall = ballWidth;
|
||||
|
||||
if (percent == 0) {
|
||||
targetXOfBigBall = leftCellFrame.origin.x + (leftCellFrame.size.width - ballWidth)/2.0;
|
||||
targetXOfSmallBall = leftCellFrame.origin.x + (leftCellFrame.size.width - targetWidthOfSmallBall)/2.0;
|
||||
}else {
|
||||
CGFloat leftX = leftCellFrame.origin.x + (leftCellFrame.size.width - ballWidth)/2;
|
||||
CGFloat rightX = rightCellFrame.origin.x + (rightCellFrame.size.width - ballWidth)/2;
|
||||
|
||||
//前50%,移动bigBall的x,缩小smallBall;后50%,移动bigBall的x,缩小smallBall,移动smallBall的x
|
||||
if (percent <= 0.5) {
|
||||
targetXOfBigBall = [JXCategoryFactory interpolationFrom:leftX to:(rightX - self.ballScrollOffsetX) percent:percent*2];
|
||||
targetWidthOfSmallBall = [JXCategoryFactory interpolationFrom:ballWidth to:ballWidth/2 percent:percent*2];
|
||||
}else {
|
||||
targetXOfBigBall = [JXCategoryFactory interpolationFrom:(rightX - self.ballScrollOffsetX) to:rightX percent:(percent - 0.5)*2];
|
||||
targetWidthOfSmallBall = [JXCategoryFactory interpolationFrom:ballWidth/2 to:0 percent:(percent - 0.5)*2];
|
||||
targetXOfSmallBall = [JXCategoryFactory interpolationFrom:leftX to:rightX percent:(percent - 0.5)*2];
|
||||
}
|
||||
}
|
||||
|
||||
//允许变动frame的情况:1、允许滚动;2、不允许滚动,但是已经通过手势滚动切换一页内容了;
|
||||
if (self.isScrollEnabled == YES || (self.isScrollEnabled == NO && percent == 0)) {
|
||||
CGRect bigBallFrame = self.bigBall.frame;
|
||||
bigBallFrame.origin.x = targetXOfBigBall;
|
||||
self.bigBall.frame = bigBallFrame;
|
||||
self.bigBall.layer.cornerRadius = bigBallFrame.size.height/2;
|
||||
|
||||
CGFloat targetYOfSmallBall = self.superview.bounds.size.height - ballHeight/2 - targetWidthOfSmallBall/2 - self.verticalMargin;
|
||||
if (self.componentPosition == JXCategoryComponentPosition_Top) {
|
||||
targetYOfSmallBall = ballHeight/2 - targetWidthOfSmallBall/2 + self.verticalMargin;
|
||||
}
|
||||
self.smallBall.frame = CGRectMake(targetXOfSmallBall, targetYOfSmallBall, targetWidthOfSmallBall, targetWidthOfSmallBall);
|
||||
self.smallBall.layer.cornerRadius = targetWidthOfSmallBall/2;
|
||||
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
self.shapeLayer.path = [self getBezierPathWithSmallCir:self.smallBall andBigCir:self.bigBall].CGPath;
|
||||
[CATransaction commit];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGFloat ballWidth = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
CGFloat ballHeight = [self indicatorHeightValue:model.selectedCellFrame];
|
||||
CGFloat x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - ballWidth)/2;
|
||||
CGFloat y = self.superview.bounds.size.height - ballHeight - self.verticalMargin;
|
||||
if (self.componentPosition == JXCategoryComponentPosition_Top) {
|
||||
y = self.verticalMargin;
|
||||
}
|
||||
CGRect toFrame = CGRectMake(x, y, ballWidth, ballHeight);
|
||||
|
||||
if (self.isScrollEnabled) {
|
||||
[UIView animateWithDuration:self.scrollAnimationDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
self.smallBall.frame = toFrame;
|
||||
self.bigBall.frame = toFrame;
|
||||
self.smallBall.layer.cornerRadius = ballHeight/2;
|
||||
self.bigBall.layer.cornerRadius = ballHeight/2;
|
||||
} completion:^(BOOL finished) {
|
||||
|
||||
}];
|
||||
}else {
|
||||
self.smallBall.frame = toFrame;
|
||||
self.bigBall.frame = toFrame;
|
||||
self.smallBall.layer.cornerRadius = ballHeight/2;
|
||||
self.bigBall.layer.cornerRadius = ballHeight/2;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIBezierPath *)getBezierPathWithSmallCir:(UIView *)smallCir andBigCir:(UIView *)bigCir{
|
||||
// 获取最小的圆
|
||||
if (bigCir.frame.size.width < smallCir.frame.size.width) {
|
||||
UIView *view = bigCir;
|
||||
bigCir = smallCir;
|
||||
smallCir = view;
|
||||
}
|
||||
// 获取小圆的信息
|
||||
CGFloat d = self.bigBall.center.x - self.smallBall.center.x;
|
||||
if (d == 0) {
|
||||
return nil;
|
||||
}
|
||||
CGFloat x1 = smallCir.center.x;
|
||||
CGFloat y1 = smallCir.center.y;
|
||||
CGFloat r1 = smallCir.bounds.size.width/2;
|
||||
|
||||
// 获取大圆的信息
|
||||
CGFloat x2 = bigCir.center.x;
|
||||
CGFloat y2 = bigCir.center.y;
|
||||
CGFloat r2 = bigCir.bounds.size.width/2;
|
||||
|
||||
// 获取三角函数
|
||||
CGFloat sinA = (y2 - y1)/d;
|
||||
CGFloat cosA = (x2 - x1)/d;
|
||||
|
||||
// 获取矩形四个点
|
||||
CGPoint pointA = CGPointMake(x1 - sinA*r1, y1 + cosA * r1);
|
||||
CGPoint pointB = CGPointMake(x1 + sinA*r1, y1 - cosA * r1);
|
||||
CGPoint pointC = CGPointMake(x2 + sinA*r2, y2 - cosA * r2);
|
||||
CGPoint pointD = CGPointMake(x2 - sinA*r2, y2 + cosA * r2);
|
||||
|
||||
// 获取控制点,以便画出曲线
|
||||
CGPoint pointO = CGPointMake(pointA.x + d / 2 * cosA , pointA.y + d / 2 * sinA);
|
||||
CGPoint pointP = CGPointMake(pointB.x + d / 2 * cosA , pointB.y + d / 2 * sinA);
|
||||
|
||||
// 创建路径
|
||||
UIBezierPath *path =[UIBezierPath bezierPath];
|
||||
[path moveToPoint:pointA];
|
||||
[path addLineToPoint:pointB];
|
||||
[path addQuadCurveToPoint:pointC controlPoint:pointP];
|
||||
[path addLineToPoint:pointD];
|
||||
[path addQuadCurveToPoint:pointA controlPoint:pointO];
|
||||
return path;
|
||||
}
|
||||
|
||||
@end
|
||||
113
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorComponentView.h
generated
Normal file
113
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorComponentView.h
generated
Normal file
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// JXCategoryComponentBaseView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryIndicatorProtocol.h"
|
||||
#import "JXCategoryViewDefines.h"
|
||||
|
||||
@interface JXCategoryIndicatorComponentView : UIView <JXCategoryIndicatorProtocol>
|
||||
|
||||
/**
|
||||
指示器的位置
|
||||
|
||||
可设置的枚举类型:
|
||||
- 底部:JXCategoryComponentPosition_Bottom
|
||||
- 顶部:JXCategoryComponentPosition_Top
|
||||
*/
|
||||
@property (nonatomic, assign) JXCategoryComponentPosition componentPosition;
|
||||
|
||||
/**
|
||||
指示器的宽度
|
||||
|
||||
默认值为 JXCategoryViewAutomaticDimension(表示与 cell 的宽度相等)。
|
||||
内部通过 `- (CGFloat)indicatorWidthValue:(CGRect)cellFrame` 方法获取实际的值。
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat indicatorWidth;
|
||||
|
||||
/**
|
||||
指示器的宽度增量
|
||||
|
||||
例如:需求是指示器宽度比 cell 宽度多 10pt。就可以将该属性赋值为 10。
|
||||
最终指示器的宽度 = indicatorWidth + indicatorWidthIncrement。
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat indicatorWidthIncrement;
|
||||
|
||||
/**
|
||||
指示器的高度
|
||||
|
||||
默认值为 3。
|
||||
内部通过 `- (CGFloat)indicatorHeightValue:(CGRect)cellFrame` 方法获取实际的值。
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat indicatorHeight;
|
||||
|
||||
/**
|
||||
指示器的 CornerRadius 圆角半径
|
||||
|
||||
默认值为 JXCategoryViewAutomaticDimension (等于 indicatorHeight/2)。
|
||||
内部通过 `- (CGFloat)indicatorCornerRadiusValue:(CGRect)cellFrame` 方法获取实际的值。
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat indicatorCornerRadius;
|
||||
|
||||
/**
|
||||
指示器的颜色
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *indicatorColor;
|
||||
|
||||
/**
|
||||
指示器在垂直方向上的偏移量
|
||||
|
||||
数值越大越靠近中心。默认值为 0。
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat verticalMargin;
|
||||
|
||||
/**
|
||||
是否允许手势滚动
|
||||
|
||||
点击切换的时候,是否允许滚动,默认值为 YES。
|
||||
*/
|
||||
@property (nonatomic, assign, getter=isScrollEnabled) BOOL scrollEnabled;
|
||||
|
||||
/**
|
||||
指示器滚动样式
|
||||
|
||||
点击切换的时候,如果允许滚动,分为简单滚动和复杂滚动。
|
||||
默认值为 JXCategoryIndicatorScrollStyleSimple
|
||||
目前仅JXCategoryIndicatorLineView、JXCategoryIndicatorDotLineView支持,其他子类暂不支持。
|
||||
*/
|
||||
@property (nonatomic, assign) JXCategoryIndicatorScrollStyle scrollStyle;
|
||||
|
||||
/**
|
||||
滚动动画的时间,默认值为 0.25s
|
||||
*/
|
||||
@property (nonatomic, assign) NSTimeInterval scrollAnimationDuration;
|
||||
|
||||
/**
|
||||
传入 cellFrame 获取指示器的最终宽度
|
||||
|
||||
@param cellFrame cellFrame
|
||||
@return 指示器的最终宽度
|
||||
*/
|
||||
- (CGFloat)indicatorWidthValue:(CGRect)cellFrame;
|
||||
|
||||
/**
|
||||
传入 cellFrame 获取指示器的最终高度
|
||||
|
||||
@param cellFrame cellFrame
|
||||
@return 指示器的最终高度
|
||||
*/
|
||||
- (CGFloat)indicatorHeightValue:(CGRect)cellFrame;
|
||||
|
||||
/**
|
||||
传入 cellFrame 获取指示器的最终圆角
|
||||
|
||||
@param cellFrame cellFrame
|
||||
@return 指示器的最终圆角
|
||||
*/
|
||||
- (CGFloat)indicatorCornerRadiusValue:(CGRect)cellFrame;
|
||||
|
||||
@end
|
||||
81
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorComponentView.m
generated
Normal file
81
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorComponentView.m
generated
Normal file
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// JXCategoryComponentBaseView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorComponentView.h"
|
||||
|
||||
@implementation JXCategoryIndicatorComponentView
|
||||
|
||||
#pragma mark - Initialize
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self configureDefaultValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self configureDefaultValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureDefaultValue {
|
||||
_componentPosition = JXCategoryComponentPosition_Bottom;
|
||||
_scrollEnabled = YES;
|
||||
_verticalMargin = 0;
|
||||
_scrollAnimationDuration = 0.25;
|
||||
_indicatorWidth = JXCategoryViewAutomaticDimension;
|
||||
_indicatorWidthIncrement = 0;
|
||||
_indicatorHeight = 3;
|
||||
_indicatorCornerRadius = JXCategoryViewAutomaticDimension;
|
||||
_indicatorColor = [UIColor redColor];
|
||||
_scrollStyle = JXCategoryIndicatorScrollStyleSimple;
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (CGFloat)indicatorWidthValue:(CGRect)cellFrame {
|
||||
if (self.indicatorWidth == JXCategoryViewAutomaticDimension) {
|
||||
return cellFrame.size.width + self.indicatorWidthIncrement;
|
||||
}
|
||||
return self.indicatorWidth + self.indicatorWidthIncrement;
|
||||
}
|
||||
|
||||
- (CGFloat)indicatorHeightValue:(CGRect)cellFrame {
|
||||
if (self.indicatorHeight == JXCategoryViewAutomaticDimension) {
|
||||
return cellFrame.size.height;
|
||||
}
|
||||
return self.indicatorHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)indicatorCornerRadiusValue:(CGRect)cellFrame {
|
||||
if (self.indicatorCornerRadius == JXCategoryViewAutomaticDimension) {
|
||||
return [self indicatorHeightValue:cellFrame]/2;
|
||||
}
|
||||
return self.indicatorCornerRadius;
|
||||
}
|
||||
|
||||
#pragma mark - JXCategoryIndicatorProtocol
|
||||
|
||||
- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model {
|
||||
|
||||
}
|
||||
|
||||
- (void)jx_contentScrollViewDidScroll:(JXCategoryIndicatorParamsModel *)model {
|
||||
|
||||
}
|
||||
|
||||
- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
17
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorDotLineView.h
generated
Normal file
17
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorDotLineView.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// JXCategoryIndicatorDotLineView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/22.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorComponentView.h"
|
||||
|
||||
/// 点线效果的指示器
|
||||
@interface JXCategoryIndicatorDotLineView : JXCategoryIndicatorComponentView
|
||||
|
||||
// 线状态的最大宽度,默认值为 50
|
||||
@property (nonatomic, assign) CGFloat lineWidth;
|
||||
|
||||
@end
|
||||
148
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorDotLineView.m
generated
Normal file
148
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorDotLineView.m
generated
Normal file
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// JXCategoryIndicatorDotLineView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/22.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorDotLineView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
#import "JXCategoryViewAnimator.h"
|
||||
|
||||
@interface JXCategoryIndicatorDotLineView ()
|
||||
@property (nonatomic, strong) JXCategoryViewAnimator *animator;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryIndicatorDotLineView
|
||||
|
||||
#pragma mark - Initialize
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self configureDefaulteValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self configureDefaulteValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureDefaulteValue {
|
||||
self.indicatorWidth = 10;
|
||||
self.indicatorHeight = 10;
|
||||
_lineWidth = 50;
|
||||
}
|
||||
|
||||
#pragma mark - JXCategoryIndicatorProtocol
|
||||
|
||||
- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGFloat dotWidth = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
CGFloat dotHeight = [self indicatorHeightValue:model.selectedCellFrame];
|
||||
self.backgroundColor = self.indicatorColor;
|
||||
self.layer.cornerRadius = [self indicatorHeightValue:model.selectedCellFrame]/2;
|
||||
|
||||
CGFloat x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - dotWidth)/2;
|
||||
CGFloat y = self.superview.bounds.size.height - dotHeight - self.verticalMargin;
|
||||
if (self.componentPosition == JXCategoryComponentPosition_Top) {
|
||||
y = self.verticalMargin;
|
||||
}
|
||||
self.frame = CGRectMake(x, y, dotWidth, dotHeight);
|
||||
}
|
||||
|
||||
- (void)jx_contentScrollViewDidScroll:(JXCategoryIndicatorParamsModel *)model {
|
||||
if (self.animator.isExecuting) {
|
||||
[self.animator invalid];
|
||||
self.animator = nil;
|
||||
}
|
||||
CGFloat dotWidth = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
CGRect rightCellFrame = model.rightCellFrame;
|
||||
CGRect leftCellFrame = model.leftCellFrame;
|
||||
CGFloat percent = model.percent;
|
||||
CGFloat targetX = 0;
|
||||
CGFloat targetWidth = dotWidth;
|
||||
CGFloat leftWidth = dotWidth;
|
||||
CGFloat rightWidth = dotWidth;
|
||||
CGFloat leftX = leftCellFrame.origin.x + (leftCellFrame.size.width - leftWidth)/2;
|
||||
CGFloat rightX = rightCellFrame.origin.x + (rightCellFrame.size.width - rightWidth)/2;
|
||||
CGFloat centerX = leftX + (rightX - leftX - self.lineWidth)/2;
|
||||
|
||||
//前50%,移动x,增加宽度;后50%,移动x并减小width
|
||||
if (percent <= 0.5) {
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:centerX percent:percent*2];
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:dotWidth to:self.lineWidth percent:percent*2];
|
||||
}else {
|
||||
targetX = [JXCategoryFactory interpolationFrom:centerX to:rightX percent:(percent - 0.5)*2];
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:self.lineWidth to:dotWidth percent:(percent - 0.5)*2];
|
||||
}
|
||||
|
||||
//允许变动frame的情况:1、允许滚动;2、不允许滚动,但是已经通过手势滚动切换一页内容了;
|
||||
if (self.isScrollEnabled == YES || (self.isScrollEnabled == NO && percent == 0)) {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.x = targetX;
|
||||
frame.size.width = targetWidth;
|
||||
self.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGFloat dotWidth = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
CGFloat x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - dotWidth)/2;
|
||||
CGRect targetIndicatorFrame = self.frame;
|
||||
targetIndicatorFrame.origin.x = x;
|
||||
if (self.isScrollEnabled) {
|
||||
if (self.scrollStyle == JXCategoryIndicatorScrollStyleSameAsUserScroll && (model.selectedType == JXCategoryCellSelectedTypeClick | model.selectedType == JXCategoryCellSelectedTypeCode)) {
|
||||
if (self.animator.isExecuting) {
|
||||
[self.animator invalid];
|
||||
self.animator = nil;
|
||||
}
|
||||
CGFloat leftX = 0;
|
||||
CGFloat rightX = 0;
|
||||
BOOL isNeedReversePercent = NO;
|
||||
if (self.frame.origin.x > model.selectedCellFrame.origin.x) {
|
||||
leftX = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - dotWidth)/2;;
|
||||
rightX = self.frame.origin.x;
|
||||
isNeedReversePercent = YES;
|
||||
}else {
|
||||
leftX = self.frame.origin.x;
|
||||
rightX = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - dotWidth)/2;
|
||||
}
|
||||
CGFloat centerX = leftX + (rightX - leftX - self.lineWidth)/2;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.animator = [[JXCategoryViewAnimator alloc] init];
|
||||
self.animator.progressCallback = ^(CGFloat percent) {
|
||||
if (isNeedReversePercent) {
|
||||
percent = 1 - percent;
|
||||
}
|
||||
CGFloat targetX = 0;
|
||||
CGFloat targetWidth = 0;
|
||||
if (percent <= 0.5) {
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:centerX percent:percent*2];
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:dotWidth to:self.lineWidth percent:percent*2];
|
||||
}else {
|
||||
targetX = [JXCategoryFactory interpolationFrom:centerX to:rightX percent:(percent - 0.5)*2];
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:self.lineWidth to:dotWidth percent:(percent - 0.5)*2];
|
||||
}
|
||||
CGRect toFrame = weakSelf.frame;
|
||||
toFrame.origin.x = targetX;
|
||||
toFrame.size.width = targetWidth;
|
||||
weakSelf.frame = toFrame;
|
||||
};
|
||||
[self.animator start];
|
||||
}else if (self.scrollStyle == JXCategoryIndicatorScrollStyleSimple) {
|
||||
[UIView animateWithDuration:self.scrollAnimationDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
self.frame = targetIndicatorFrame;
|
||||
} completion: nil];
|
||||
}
|
||||
}else {
|
||||
self.frame = targetIndicatorFrame;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
20
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorImageView.h
generated
Normal file
20
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorImageView.h
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// JXCategoryIndicatorImageView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorComponentView.h"
|
||||
|
||||
@interface JXCategoryIndicatorImageView : JXCategoryIndicatorComponentView
|
||||
|
||||
// 指示器图片
|
||||
@property (nonatomic, strong, readonly) UIImageView *indicatorImageView;
|
||||
// 图片是否开启滚动,默认值为 NO
|
||||
@property (nonatomic, assign) BOOL indicatorImageViewRollEnabled;
|
||||
// 图片的尺寸,默认值为 CGSizeMake(30, 20)
|
||||
@property (nonatomic, assign) CGSize indicatorImageViewSize;
|
||||
|
||||
@end
|
||||
117
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorImageView.m
generated
Normal file
117
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorImageView.m
generated
Normal file
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// JXCategoryIndicatorImageView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorImageView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@implementation JXCategoryIndicatorImageView
|
||||
|
||||
#pragma mark - Initialize
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self setupIndicatorImageView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self setupIndicatorImageView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupIndicatorImageView {
|
||||
_indicatorImageViewSize = CGSizeMake(30, 20);
|
||||
_indicatorImageViewRollEnabled = NO;
|
||||
|
||||
_indicatorImageView = [[UIImageView alloc] init];
|
||||
self.indicatorImageView.frame = CGRectMake(0, 0, self.indicatorImageViewSize.width, self.indicatorImageViewSize.height);
|
||||
self.indicatorImageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
[self addSubview:self.indicatorImageView];
|
||||
}
|
||||
|
||||
#pragma mark - Custom Accessors
|
||||
|
||||
- (void)setIndicatorImageViewSize:(CGSize)indicatorImageViewSize {
|
||||
_indicatorImageViewSize = indicatorImageViewSize;
|
||||
|
||||
self.indicatorImageView.frame = CGRectMake(0, 0, self.indicatorImageViewSize.width, self.indicatorImageViewSize.height);
|
||||
}
|
||||
|
||||
#pragma mark - JXCategoryIndicatorProtocol
|
||||
|
||||
- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGFloat x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - self.indicatorImageViewSize.width)/2;
|
||||
CGFloat y = self.superview.bounds.size.height - self.indicatorImageViewSize.height - self.verticalMargin;
|
||||
if (self.componentPosition == JXCategoryComponentPosition_Top) {
|
||||
y = self.verticalMargin;
|
||||
}
|
||||
self.frame = CGRectMake(x, y, self.indicatorImageViewSize.width, self.indicatorImageViewSize.height);
|
||||
}
|
||||
|
||||
- (void)jx_contentScrollViewDidScroll:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGRect rightCellFrame = model.rightCellFrame;
|
||||
CGRect leftCellFrame = model.leftCellFrame;
|
||||
CGFloat percent = model.percent;
|
||||
CGFloat targetWidth = self.indicatorImageViewSize.width;
|
||||
CGFloat targetX = 0;
|
||||
|
||||
if (percent == 0) {
|
||||
targetX = leftCellFrame.origin.x + (leftCellFrame.size.width - targetWidth)/2.0;
|
||||
}else {
|
||||
CGFloat leftX = leftCellFrame.origin.x + (leftCellFrame.size.width - targetWidth)/2;
|
||||
CGFloat rightX = rightCellFrame.origin.x + (rightCellFrame.size.width - targetWidth)/2;
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:rightX percent:percent];
|
||||
}
|
||||
|
||||
//允许变动frame的情况:1、允许滚动;2、不允许滚动,但是已经通过手势滚动切换一页内容了;
|
||||
if (self.isScrollEnabled == YES || (self.isScrollEnabled == NO && percent == 0)) {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.x = targetX;
|
||||
self.frame = frame;
|
||||
|
||||
if (self.indicatorImageViewRollEnabled) {
|
||||
self.indicatorImageView.transform = CGAffineTransformMakeRotation(M_PI*2*percent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGRect toFrame = self.frame;
|
||||
toFrame.origin.x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - self.indicatorImageViewSize.width)/2;
|
||||
if (self.isScrollEnabled) {
|
||||
[UIView animateWithDuration:self.scrollAnimationDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
|
||||
self.frame = toFrame;
|
||||
} completion:^(BOOL finished) {
|
||||
}];
|
||||
if (self.indicatorImageViewRollEnabled && (model.selectedType == JXCategoryCellSelectedTypeCode || model.selectedType == JXCategoryCellSelectedTypeClick)) {
|
||||
[self.indicatorImageView.layer removeAnimationForKey:@"rotate"];
|
||||
CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
|
||||
if (model.selectedIndex > model.lastSelectedIndex) {
|
||||
rotateAnimation.fromValue = @(0);
|
||||
rotateAnimation.toValue = @(M_PI*2);
|
||||
}else {
|
||||
rotateAnimation.fromValue = @(M_PI*2);
|
||||
rotateAnimation.toValue = @(0);
|
||||
}
|
||||
rotateAnimation.fillMode = kCAFillModeBackwards;
|
||||
rotateAnimation.removedOnCompletion = YES;
|
||||
rotateAnimation.duration = 0.25;
|
||||
[self.indicatorImageView.layer addAnimation:rotateAnimation forKey:@"rotate"];
|
||||
}
|
||||
}else {
|
||||
self.frame = toFrame;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
28
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorLineView.h
generated
Normal file
28
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorLineView.h
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// JXCategoryIndicatorLineView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorComponentView.h"
|
||||
|
||||
typedef NS_ENUM(NSUInteger, JXCategoryIndicatorLineStyle) {
|
||||
JXCategoryIndicatorLineStyle_Normal = 0,
|
||||
JXCategoryIndicatorLineStyle_Lengthen = 1,
|
||||
JXCategoryIndicatorLineStyle_LengthenOffset = 2,
|
||||
};
|
||||
|
||||
@interface JXCategoryIndicatorLineView : JXCategoryIndicatorComponentView
|
||||
|
||||
@property (nonatomic, assign) JXCategoryIndicatorLineStyle lineStyle;
|
||||
|
||||
/**
|
||||
line 滚动时沿 x 轴方向上的偏移量,默认值为 10。
|
||||
|
||||
lineStyle 为 JXCategoryIndicatorLineStyle_LengthenOffset 有用。
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat lineScrollOffsetX;
|
||||
|
||||
@end
|
||||
202
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorLineView.m
generated
Normal file
202
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorLineView.m
generated
Normal file
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// JXCategoryIndicatorLineView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorLineView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
#import "JXCategoryViewDefines.h"
|
||||
#import "JXCategoryViewAnimator.h"
|
||||
|
||||
@interface JXCategoryIndicatorLineView ()
|
||||
@property (nonatomic, strong) JXCategoryViewAnimator *animator;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryIndicatorLineView
|
||||
|
||||
#pragma mark - Initialize
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self configureDefaulteValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self configureDefaulteValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureDefaulteValue {
|
||||
_lineStyle = JXCategoryIndicatorLineStyle_Normal;
|
||||
_lineScrollOffsetX = 10;
|
||||
self.indicatorHeight = 3;
|
||||
}
|
||||
|
||||
#pragma mark - JXCategoryIndicatorProtocol
|
||||
|
||||
- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model {
|
||||
self.backgroundColor = self.indicatorColor;
|
||||
self.layer.cornerRadius = [self indicatorCornerRadiusValue:model.selectedCellFrame];
|
||||
|
||||
CGFloat selectedLineWidth = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
CGFloat x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - selectedLineWidth)/2;
|
||||
CGFloat y = self.superview.bounds.size.height - [self indicatorHeightValue:model.selectedCellFrame] - self.verticalMargin;
|
||||
if (self.componentPosition == JXCategoryComponentPosition_Top) {
|
||||
y = self.verticalMargin;
|
||||
}
|
||||
self.frame = CGRectMake(x, y, selectedLineWidth, [self indicatorHeightValue:model.selectedCellFrame]);
|
||||
}
|
||||
|
||||
- (void)jx_contentScrollViewDidScroll:(JXCategoryIndicatorParamsModel *)model {
|
||||
if (self.animator.isExecuting) {
|
||||
[self.animator invalid];
|
||||
self.animator = nil;
|
||||
}
|
||||
CGRect rightCellFrame = model.rightCellFrame;
|
||||
CGRect leftCellFrame = model.leftCellFrame;
|
||||
CGFloat percent = model.percent;
|
||||
CGFloat targetX = leftCellFrame.origin.x;
|
||||
CGFloat targetWidth = [self indicatorWidthValue:leftCellFrame];
|
||||
|
||||
CGFloat leftWidth = targetWidth;
|
||||
CGFloat rightWidth = [self indicatorWidthValue:rightCellFrame];
|
||||
CGFloat leftX = leftCellFrame.origin.x + (leftCellFrame.size.width - leftWidth)/2;
|
||||
CGFloat rightX = rightCellFrame.origin.x + (rightCellFrame.size.width - rightWidth)/2;
|
||||
|
||||
if (self.lineStyle == JXCategoryIndicatorLineStyle_Normal) {
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:rightX percent:percent];
|
||||
if (self.indicatorWidth == JXCategoryViewAutomaticDimension) {
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:leftWidth to:rightWidth percent:percent];
|
||||
}
|
||||
}else if (self.lineStyle == JXCategoryIndicatorLineStyle_Lengthen) {
|
||||
CGFloat maxWidth = rightX - leftX + rightWidth;
|
||||
//前50%,只增加width;后50%,移动x并减小width
|
||||
if (percent <= 0.5) {
|
||||
targetX = leftX;
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:leftWidth to:maxWidth percent:percent*2];
|
||||
}else {
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:rightX percent:(percent - 0.5)*2];
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:maxWidth to:rightWidth percent:(percent - 0.5)*2];
|
||||
}
|
||||
}else if (self.lineStyle == JXCategoryIndicatorLineStyle_LengthenOffset) {
|
||||
//前50%,增加width,并少量移动x;后50%,少量移动x并减小width
|
||||
CGFloat offsetX = self.lineScrollOffsetX;//x的少量偏移量
|
||||
CGFloat maxWidth = rightX - leftX + rightWidth - offsetX*2;
|
||||
if (percent <= 0.5) {
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:leftX + offsetX percent:percent*2];;
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:leftWidth to:maxWidth percent:percent*2];
|
||||
}else {
|
||||
targetX = [JXCategoryFactory interpolationFrom:(leftX + offsetX) to:rightX percent:(percent - 0.5)*2];
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:maxWidth to:rightWidth percent:(percent - 0.5)*2];
|
||||
}
|
||||
}
|
||||
//允许变动frame的情况:1、允许滚动;2、不允许滚动,但是已经通过手势滚动切换一页内容了;
|
||||
if (self.isScrollEnabled == YES || (self.isScrollEnabled == NO && percent == 0)) {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.x = targetX;
|
||||
frame.size.width = targetWidth;
|
||||
self.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGRect targetIndicatorFrame = self.frame;
|
||||
CGFloat targetIndicatorWidth = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
targetIndicatorFrame.origin.x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - targetIndicatorWidth)/2.0;
|
||||
targetIndicatorFrame.size.width = targetIndicatorWidth;
|
||||
if (self.isScrollEnabled) {
|
||||
if (self.scrollStyle == JXCategoryIndicatorScrollStyleSameAsUserScroll && (model.selectedType == JXCategoryCellSelectedTypeClick | model.selectedType == JXCategoryCellSelectedTypeCode)) {
|
||||
if (self.animator.isExecuting) {
|
||||
[self.animator invalid];
|
||||
self.animator = nil;
|
||||
}
|
||||
CGFloat leftX = 0;
|
||||
CGFloat rightX = 0;
|
||||
CGFloat leftWidth = 0;
|
||||
CGFloat rightWidth = 0;
|
||||
BOOL isNeedReversePercent = NO;
|
||||
if (self.frame.origin.x > model.selectedCellFrame.origin.x) {
|
||||
leftWidth = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
rightWidth = self.frame.size.width;
|
||||
leftX = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - leftWidth)/2;;
|
||||
rightX = self.frame.origin.x;
|
||||
isNeedReversePercent = YES;
|
||||
}else {
|
||||
leftWidth = self.frame.size.width;
|
||||
rightWidth = [self indicatorWidthValue:model.selectedCellFrame];
|
||||
leftX = self.frame.origin.x;
|
||||
rightX = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - rightWidth)/2;
|
||||
}
|
||||
__weak typeof(self) weakSelf = self;
|
||||
if (self.lineStyle == JXCategoryIndicatorLineStyle_Normal) {
|
||||
[UIView animateWithDuration:self.scrollAnimationDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
self.frame = targetIndicatorFrame;
|
||||
} completion: nil];
|
||||
}else if (self.lineStyle == JXCategoryIndicatorLineStyle_Lengthen) {
|
||||
CGFloat maxWidth = rightX - leftX + rightWidth;
|
||||
//前50%,只增加width;后50%,移动x并减小width
|
||||
self.animator = [[JXCategoryViewAnimator alloc] init];
|
||||
self.animator.progressCallback = ^(CGFloat percent) {
|
||||
if (isNeedReversePercent) {
|
||||
percent = 1 - percent;
|
||||
}
|
||||
CGFloat targetX = 0;
|
||||
CGFloat targetWidth = 0;
|
||||
if (percent <= 0.5) {
|
||||
targetX = leftX;
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:leftWidth to:maxWidth percent:percent*2];
|
||||
}else {
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:rightX percent:(percent - 0.5)*2];
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:maxWidth to:rightWidth percent:(percent - 0.5)*2];
|
||||
}
|
||||
CGRect toFrame = weakSelf.frame;
|
||||
toFrame.origin.x = targetX;
|
||||
toFrame.size.width = targetWidth;
|
||||
weakSelf.frame = toFrame;
|
||||
};
|
||||
[self.animator start];
|
||||
}else if (self.lineStyle == JXCategoryIndicatorLineStyle_LengthenOffset) {
|
||||
//前50%,增加width,并少量移动x;后50%,少量移动x并减小width
|
||||
CGFloat offsetX = self.lineScrollOffsetX;//x的少量偏移量
|
||||
CGFloat maxWidth = rightX - leftX + rightWidth - offsetX*2;
|
||||
self.animator = [[JXCategoryViewAnimator alloc] init];
|
||||
self.animator.progressCallback = ^(CGFloat percent) {
|
||||
if (isNeedReversePercent) {
|
||||
percent = 1 - percent;
|
||||
}
|
||||
CGFloat targetX = 0;
|
||||
CGFloat targetWidth = 0;
|
||||
if (percent <= 0.5) {
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:leftX + offsetX percent:percent*2];;
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:leftWidth to:maxWidth percent:percent*2];
|
||||
}else {
|
||||
targetX = [JXCategoryFactory interpolationFrom:(leftX + offsetX) to:rightX percent:(percent - 0.5)*2];
|
||||
targetWidth = [JXCategoryFactory interpolationFrom:maxWidth to:rightWidth percent:(percent - 0.5)*2];
|
||||
}
|
||||
CGRect toFrame = weakSelf.frame;
|
||||
toFrame.origin.x = targetX;
|
||||
toFrame.size.width = targetWidth;
|
||||
weakSelf.frame = toFrame;
|
||||
};
|
||||
[self.animator start];
|
||||
}
|
||||
}else if (self.scrollStyle == JXCategoryIndicatorScrollStyleSimple) {
|
||||
[UIView animateWithDuration:self.scrollAnimationDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
self.frame = targetIndicatorFrame;
|
||||
} completion: nil];
|
||||
}
|
||||
}else {
|
||||
self.frame = targetIndicatorFrame;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
30
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorRainbowLineView.h
generated
Normal file
30
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorRainbowLineView.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// JXCategoryIndicatorRainbowLineView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/12/13.
|
||||
// Copyright © 2018 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorLineView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
彩虹效果的指示器
|
||||
|
||||
!!!: 会无视 JXCategoryIndicatorLineView 的 indicatorColor 属性,以 indicatorColors 为准。
|
||||
*/
|
||||
@interface JXCategoryIndicatorRainbowLineView : JXCategoryIndicatorLineView
|
||||
|
||||
/**
|
||||
指示器颜色数组
|
||||
|
||||
数量需要与 cell 的数量相等。没有提供默认值,必须要赋值该属性。
|
||||
categoryView 在 reloadData 的时候,也要一并更新该属性,不然会出现数组越界。
|
||||
*/
|
||||
@property (nonatomic, strong) NSArray <UIColor *> *indicatorColors;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
38
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorRainbowLineView.m
generated
Normal file
38
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorRainbowLineView.m
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// JXCategoryIndicatorRainbowLineView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/12/13.
|
||||
// Copyright © 2018 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorRainbowLineView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@implementation JXCategoryIndicatorRainbowLineView
|
||||
|
||||
- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model {
|
||||
[super jx_refreshState:model];
|
||||
|
||||
UIColor *color = self.indicatorColors[model.selectedIndex];
|
||||
self.backgroundColor = color;
|
||||
}
|
||||
|
||||
- (void)jx_contentScrollViewDidScroll:(JXCategoryIndicatorParamsModel *)model {
|
||||
[super jx_contentScrollViewDidScroll:model];
|
||||
|
||||
UIColor *leftColor = self.indicatorColors[model.leftIndex];
|
||||
UIColor *rightColor = self.indicatorColors[model.rightIndex];
|
||||
UIColor *color = [JXCategoryFactory interpolationColorFrom:leftColor to:rightColor percent:model.percent];
|
||||
self.backgroundColor = color;
|
||||
}
|
||||
|
||||
- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model {
|
||||
[super jx_selectedCell:model];
|
||||
|
||||
UIColor *color = self.indicatorColors[model.selectedIndex];
|
||||
self.backgroundColor = color;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
14
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorTriangleView.h
generated
Normal file
14
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorTriangleView.h
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// JXCategoryIndicatorTriangleView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorComponentView.h"
|
||||
|
||||
/// 三角形样式的指示器
|
||||
@interface JXCategoryIndicatorTriangleView : JXCategoryIndicatorComponentView
|
||||
|
||||
@end
|
||||
109
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorTriangleView.m
generated
Normal file
109
Pods/JXCategoryView/Sources/Indicator/IndicatorViews/JXCategoryIndicatorTriangleView.m
generated
Normal file
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// JXCategoryIndicatorTriangleView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/17.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorTriangleView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@interface JXCategoryIndicatorTriangleView ()
|
||||
@property (nonatomic, strong) CAShapeLayer *triangleLayer;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryIndicatorTriangleView
|
||||
|
||||
#pragma mark - Initialize
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self configureDefaulteValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
if (self) {
|
||||
[self configureDefaulteValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureDefaulteValue {
|
||||
self.indicatorWidth = 14;
|
||||
self.indicatorHeight = 10;
|
||||
|
||||
_triangleLayer = [CAShapeLayer layer];
|
||||
[self.layer addSublayer:self.triangleLayer];
|
||||
}
|
||||
|
||||
#pragma mark - JXCategoryIndicatorProtocol
|
||||
|
||||
- (void)jx_refreshState:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGFloat x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - [self indicatorWidthValue:model.selectedCellFrame])/2;
|
||||
CGFloat y = self.superview.bounds.size.height - [self indicatorHeightValue:model.selectedCellFrame] - self.verticalMargin;
|
||||
if (self.componentPosition == JXCategoryComponentPosition_Top) {
|
||||
y = self.verticalMargin;
|
||||
}
|
||||
self.frame = CGRectMake(x, y, [self indicatorWidthValue:model.selectedCellFrame], [self indicatorHeightValue:model.selectedCellFrame]);
|
||||
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:NO];
|
||||
self.triangleLayer.fillColor = self.indicatorColor.CGColor;
|
||||
self.triangleLayer.frame = self.bounds;
|
||||
UIBezierPath *path = [UIBezierPath bezierPath];
|
||||
if (self.componentPosition == JXCategoryComponentPosition_Bottom) {
|
||||
[path moveToPoint:CGPointMake(self.bounds.size.width/2, 0)];
|
||||
[path addLineToPoint:CGPointMake(0, self.bounds.size.height)];
|
||||
[path addLineToPoint:CGPointMake(self.bounds.size.width, self.bounds.size.height)];
|
||||
} else {
|
||||
[path moveToPoint:CGPointMake(0, 0)];
|
||||
[path addLineToPoint:CGPointMake(self.bounds.size.width, 0)];
|
||||
[path addLineToPoint:CGPointMake(self.bounds.size.width/2, self.bounds.size.height)];
|
||||
}
|
||||
[path closePath];
|
||||
self.triangleLayer.path = path.CGPath;
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
- (void)jx_contentScrollViewDidScroll:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGRect rightCellFrame = model.rightCellFrame;
|
||||
CGRect leftCellFrame = model.leftCellFrame;
|
||||
CGFloat percent = model.percent;
|
||||
CGFloat targetWidth = [self indicatorWidthValue:model.leftCellFrame];
|
||||
CGFloat targetX = 0;
|
||||
|
||||
if (percent == 0) {
|
||||
targetX = leftCellFrame.origin.x + (leftCellFrame.size.width - targetWidth)/2.0;
|
||||
} else {
|
||||
CGFloat leftX = leftCellFrame.origin.x + (leftCellFrame.size.width - targetWidth)/2;
|
||||
CGFloat rightX = rightCellFrame.origin.x + (rightCellFrame.size.width - targetWidth)/2;
|
||||
targetX = [JXCategoryFactory interpolationFrom:leftX to:rightX percent:percent];
|
||||
}
|
||||
|
||||
//允许变动frame的情况:1、允许滚动;2、不允许滚动,但是已经通过手势滚动切换一页内容了;
|
||||
if (self.isScrollEnabled == YES || (self.isScrollEnabled == NO && percent == 0)) {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.x = targetX;
|
||||
self.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)jx_selectedCell:(JXCategoryIndicatorParamsModel *)model {
|
||||
CGRect toFrame = self.frame;
|
||||
toFrame.origin.x = model.selectedCellFrame.origin.x + (model.selectedCellFrame.size.width - [self indicatorWidthValue:model.selectedCellFrame])/2;
|
||||
if (self.isScrollEnabled) {
|
||||
[UIView animateWithDuration:self.scrollAnimationDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
|
||||
self.frame = toFrame;
|
||||
} completion:^(BOOL finished) {
|
||||
}];
|
||||
} else {
|
||||
self.frame = toFrame;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
13
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorCell.h
generated
Normal file
13
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorCell.h
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryComponetCell.h
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/7/25.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryBaseCell.h"
|
||||
|
||||
@interface JXCategoryIndicatorCell : JXCategoryBaseCell
|
||||
|
||||
@end
|
||||
52
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorCell.m
generated
Normal file
52
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorCell.m
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// JXCategoryComponetCell.m
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/7/25.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorCell.h"
|
||||
#import "JXCategoryIndicatorCellModel.h"
|
||||
|
||||
@interface JXCategoryIndicatorCell ()
|
||||
@property (nonatomic, strong) UIView *separatorLine;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryIndicatorCell
|
||||
|
||||
- (void)initializeViews {
|
||||
[super initializeViews];
|
||||
|
||||
self.separatorLine = [[UIView alloc] init];
|
||||
self.separatorLine.hidden = YES;
|
||||
[self.contentView addSubview:self.separatorLine];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
JXCategoryIndicatorCellModel *model = (JXCategoryIndicatorCellModel *)self.cellModel;
|
||||
CGFloat lineWidth = model.separatorLineSize.width;
|
||||
CGFloat lineHeight = model.separatorLineSize.height;
|
||||
|
||||
self.separatorLine.frame = CGRectMake(self.bounds.size.width - lineWidth + self.cellModel.cellSpacing/2, (self.bounds.size.height - lineHeight)/2.0, lineWidth, lineHeight);
|
||||
}
|
||||
|
||||
- (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
|
||||
[super reloadData:cellModel];
|
||||
|
||||
JXCategoryIndicatorCellModel *model = (JXCategoryIndicatorCellModel *)cellModel;
|
||||
self.separatorLine.backgroundColor = model.separatorLineColor;
|
||||
self.separatorLine.hidden = !model.isSepratorLineShowEnabled;
|
||||
|
||||
if (model.isCellBackgroundColorGradientEnabled) {
|
||||
if (model.isSelected) {
|
||||
self.contentView.backgroundColor = model.cellBackgroundSelectedColor;
|
||||
}else {
|
||||
self.contentView.backgroundColor = model.cellBackgroundUnselectedColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
24
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorCellModel.h
generated
Normal file
24
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorCellModel.h
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// JXCategoryComponentCellModel.h
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/7/25.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryBaseCellModel.h"
|
||||
|
||||
@interface JXCategoryIndicatorCellModel : JXCategoryBaseCellModel
|
||||
|
||||
@property (nonatomic, assign, getter=isSepratorLineShowEnabled) BOOL sepratorLineShowEnabled;
|
||||
@property (nonatomic, strong) UIColor *separatorLineColor;
|
||||
@property (nonatomic, assign) CGSize separatorLineSize;
|
||||
|
||||
@property (nonatomic, assign) CGRect backgroundViewMaskFrame; // 底部指示器的 frame 转换到 cell 的 frame
|
||||
|
||||
@property (nonatomic, assign, getter=isCellBackgroundColorGradientEnabled) BOOL cellBackgroundColorGradientEnabled;
|
||||
@property (nonatomic, strong) UIColor *cellBackgroundSelectedColor;
|
||||
@property (nonatomic, strong) UIColor *cellBackgroundUnselectedColor;
|
||||
|
||||
@end
|
||||
13
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorCellModel.m
generated
Normal file
13
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorCellModel.m
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryComponentCellModel.m
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/7/25.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorCellModel.h"
|
||||
|
||||
@implementation JXCategoryIndicatorCellModel
|
||||
|
||||
@end
|
||||
48
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorView.h
generated
Normal file
48
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorView.h
generated
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// JXCategoryComponentView.h
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/7/25.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryBaseView.h"
|
||||
#import "JXCategoryIndicatorCell.h"
|
||||
#import "JXCategoryIndicatorCellModel.h"
|
||||
#import "JXCategoryIndicatorProtocol.h"
|
||||
|
||||
@interface JXCategoryIndicatorView : JXCategoryBaseView
|
||||
|
||||
@property (nonatomic, strong) NSArray <UIView<JXCategoryIndicatorProtocol> *> *indicators;
|
||||
|
||||
//----------------------ellBackgroundColor-----------------------//
|
||||
//cell的背景色是否渐变。默认:NO
|
||||
@property (nonatomic, assign, getter=isCellBackgroundColorGradientEnabled) BOOL cellBackgroundColorGradientEnabled;
|
||||
//cell普通状态的背景色。默认:[UIColor clearColor]
|
||||
@property (nonatomic, strong) UIColor *cellBackgroundUnselectedColor;
|
||||
//cell选中状态的背景色。默认:[UIColor grayColor]
|
||||
@property (nonatomic, strong) UIColor *cellBackgroundSelectedColor;
|
||||
|
||||
//----------------------separatorLine-----------------------//
|
||||
//是否显示分割线。默认为NO
|
||||
@property (nonatomic, assign, getter=isSeparatorLineShowEnabled) BOOL separatorLineShowEnabled;
|
||||
//分割线颜色。默认为[UIColor lightGrayColor]
|
||||
@property (nonatomic, strong) UIColor *separatorLineColor;
|
||||
//分割线的size。默认为CGSizeMake(1/[UIScreen mainScreen].scale, 20)
|
||||
@property (nonatomic, assign) CGSize separatorLineSize;
|
||||
|
||||
@end
|
||||
|
||||
@interface JXCategoryIndicatorView (UISubclassingIndicatorHooks)
|
||||
|
||||
/**
|
||||
当contentScrollView滚动时候,处理跟随手势的过渡效果。
|
||||
根据cellModel的左右位置、是否选中、ratio进行过滤数据计算。
|
||||
|
||||
@param leftCellModel 左边的cellModel
|
||||
@param rightCellModel 右边的cellModel
|
||||
@param ratio 从左往右方向计算的百分比
|
||||
*/
|
||||
- (void)refreshLeftCellModel:(JXCategoryBaseCellModel *)leftCellModel rightCellModel:(JXCategoryBaseCellModel *)rightCellModel ratio:(CGFloat)ratio NS_REQUIRES_SUPER;
|
||||
|
||||
@end
|
||||
209
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorView.m
generated
Normal file
209
Pods/JXCategoryView/Sources/Indicator/JXCategoryIndicatorView.m
generated
Normal file
@@ -0,0 +1,209 @@
|
||||
//
|
||||
// JXCategoryIndicatorView.m
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/7/25.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorView.h"
|
||||
#import "JXCategoryIndicatorBackgroundView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@interface JXCategoryIndicatorView()
|
||||
|
||||
@end
|
||||
|
||||
@implementation JXCategoryIndicatorView
|
||||
|
||||
- (void)initializeData {
|
||||
[super initializeData];
|
||||
|
||||
_separatorLineShowEnabled = NO;
|
||||
_separatorLineColor = [UIColor lightGrayColor];
|
||||
_separatorLineSize = CGSizeMake(1/[UIScreen mainScreen].scale, 20);
|
||||
_cellBackgroundColorGradientEnabled = NO;
|
||||
_cellBackgroundUnselectedColor = [UIColor whiteColor];
|
||||
_cellBackgroundSelectedColor = [UIColor lightGrayColor];
|
||||
}
|
||||
|
||||
- (void)initializeViews {
|
||||
[super initializeViews];
|
||||
}
|
||||
|
||||
- (void)setIndicators:(NSArray<UIView<JXCategoryIndicatorProtocol> *> *)indicators {
|
||||
_indicators = indicators;
|
||||
|
||||
self.collectionView.indicators = indicators;
|
||||
}
|
||||
|
||||
- (void)refreshState {
|
||||
[super refreshState];
|
||||
|
||||
CGRect selectedCellFrame = CGRectZero;
|
||||
JXCategoryIndicatorCellModel *selectedCellModel;
|
||||
for (int i = 0; i < self.dataSource.count; i++) {
|
||||
JXCategoryIndicatorCellModel *cellModel = (JXCategoryIndicatorCellModel *)self.dataSource[i];
|
||||
cellModel.sepratorLineShowEnabled = self.isSeparatorLineShowEnabled;
|
||||
cellModel.separatorLineColor = self.separatorLineColor;
|
||||
cellModel.separatorLineSize = self.separatorLineSize;
|
||||
cellModel.backgroundViewMaskFrame = CGRectZero;
|
||||
cellModel.cellBackgroundColorGradientEnabled = self.isCellBackgroundColorGradientEnabled;
|
||||
cellModel.cellBackgroundSelectedColor = self.cellBackgroundSelectedColor;
|
||||
cellModel.cellBackgroundUnselectedColor = self.cellBackgroundUnselectedColor;
|
||||
if (i == self.dataSource.count - 1) {
|
||||
cellModel.sepratorLineShowEnabled = NO;
|
||||
}
|
||||
if (i == self.selectedIndex) {
|
||||
selectedCellModel = cellModel;
|
||||
selectedCellFrame = [self getTargetCellFrame:i];
|
||||
}
|
||||
}
|
||||
|
||||
for (UIView<JXCategoryIndicatorProtocol> *indicator in self.indicators) {
|
||||
if (self.dataSource.count <= 0) {
|
||||
indicator.hidden = YES;
|
||||
} else {
|
||||
indicator.hidden = NO;
|
||||
JXCategoryIndicatorParamsModel *indicatorParamsModel = [[JXCategoryIndicatorParamsModel alloc] init];
|
||||
indicatorParamsModel.selectedIndex = self.selectedIndex;
|
||||
indicatorParamsModel.selectedCellFrame = selectedCellFrame;
|
||||
[indicator jx_refreshState:indicatorParamsModel];
|
||||
|
||||
if ([indicator isKindOfClass:[JXCategoryIndicatorBackgroundView class]]) {
|
||||
CGRect maskFrame = indicator.frame;
|
||||
maskFrame.origin.x = maskFrame.origin.x - selectedCellFrame.origin.x;
|
||||
selectedCellModel.backgroundViewMaskFrame = maskFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshSelectedCellModel:(JXCategoryBaseCellModel *)selectedCellModel unselectedCellModel:(JXCategoryBaseCellModel *)unselectedCellModel {
|
||||
[super refreshSelectedCellModel:selectedCellModel unselectedCellModel:unselectedCellModel];
|
||||
|
||||
JXCategoryIndicatorCellModel *myUnselectedCellModel = (JXCategoryIndicatorCellModel *)unselectedCellModel;
|
||||
myUnselectedCellModel.backgroundViewMaskFrame = CGRectZero;
|
||||
myUnselectedCellModel.cellBackgroundUnselectedColor = self.cellBackgroundUnselectedColor;
|
||||
myUnselectedCellModel.cellBackgroundSelectedColor = self.cellBackgroundSelectedColor;
|
||||
|
||||
JXCategoryIndicatorCellModel *myselectedCellModel = (JXCategoryIndicatorCellModel *)selectedCellModel;
|
||||
myselectedCellModel.cellBackgroundUnselectedColor = self.cellBackgroundUnselectedColor;
|
||||
myselectedCellModel.cellBackgroundSelectedColor = self.cellBackgroundSelectedColor;
|
||||
}
|
||||
|
||||
- (void)contentOffsetOfContentScrollViewDidChanged:(CGPoint)contentOffset {
|
||||
[super contentOffsetOfContentScrollViewDidChanged:contentOffset];
|
||||
|
||||
CGFloat ratio = contentOffset.x/self.contentScrollView.bounds.size.width;
|
||||
if (ratio > self.dataSource.count - 1 || ratio < 0) {
|
||||
//超过了边界,不需要处理
|
||||
return;
|
||||
}
|
||||
ratio = MAX(0, MIN(self.dataSource.count - 1, ratio));
|
||||
NSInteger baseIndex = floorf(ratio);
|
||||
if (baseIndex + 1 >= self.dataSource.count) {
|
||||
//右边越界了,不需要处理
|
||||
return;
|
||||
}
|
||||
CGFloat remainderRatio = ratio - baseIndex;
|
||||
|
||||
CGRect leftCellFrame = [self getTargetCellFrame:baseIndex];
|
||||
CGRect rightCellFrame = [self getTargetCellFrame:baseIndex + 1];
|
||||
|
||||
JXCategoryIndicatorParamsModel *indicatorParamsModel = [[JXCategoryIndicatorParamsModel alloc] init];
|
||||
indicatorParamsModel.selectedIndex = self.selectedIndex;
|
||||
indicatorParamsModel.leftIndex = baseIndex;
|
||||
indicatorParamsModel.leftCellFrame = leftCellFrame;
|
||||
indicatorParamsModel.rightIndex = baseIndex + 1;
|
||||
indicatorParamsModel.rightCellFrame = rightCellFrame;
|
||||
indicatorParamsModel.percent = remainderRatio;
|
||||
if (remainderRatio == 0) {
|
||||
for (UIView<JXCategoryIndicatorProtocol> *indicator in self.indicators) {
|
||||
[indicator jx_contentScrollViewDidScroll:indicatorParamsModel];
|
||||
}
|
||||
} else {
|
||||
JXCategoryIndicatorCellModel *leftCellModel = (JXCategoryIndicatorCellModel *)self.dataSource[baseIndex];
|
||||
leftCellModel.selectedType = JXCategoryCellSelectedTypeUnknown;
|
||||
JXCategoryIndicatorCellModel *rightCellModel = (JXCategoryIndicatorCellModel *)self.dataSource[baseIndex + 1];
|
||||
rightCellModel.selectedType = JXCategoryCellSelectedTypeUnknown;
|
||||
[self refreshLeftCellModel:leftCellModel rightCellModel:rightCellModel ratio:remainderRatio];
|
||||
|
||||
for (UIView<JXCategoryIndicatorProtocol> *indicator in self.indicators) {
|
||||
[indicator jx_contentScrollViewDidScroll:indicatorParamsModel];
|
||||
if ([indicator isKindOfClass:[JXCategoryIndicatorBackgroundView class]]) {
|
||||
CGRect leftMaskFrame = indicator.frame;
|
||||
leftMaskFrame.origin.x = leftMaskFrame.origin.x - leftCellFrame.origin.x;
|
||||
leftCellModel.backgroundViewMaskFrame = leftMaskFrame;
|
||||
|
||||
CGRect rightMaskFrame = indicator.frame;
|
||||
rightMaskFrame.origin.x = rightMaskFrame.origin.x - rightCellFrame.origin.x;
|
||||
rightCellModel.backgroundViewMaskFrame = rightMaskFrame;
|
||||
}
|
||||
}
|
||||
|
||||
JXCategoryBaseCell *leftCell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:baseIndex inSection:0]];
|
||||
[leftCell reloadData:leftCellModel];
|
||||
JXCategoryBaseCell *rightCell = (JXCategoryBaseCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:baseIndex + 1 inSection:0]];
|
||||
[rightCell reloadData:rightCellModel];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)selectCellAtIndex:(NSInteger)index selectedType:(JXCategoryCellSelectedType)selectedType {
|
||||
NSInteger lastSelectedIndex = self.selectedIndex;
|
||||
BOOL result = [super selectCellAtIndex:index selectedType:selectedType];
|
||||
if (!result) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
CGRect clickedCellFrame = [self getTargetSelectedCellFrame:index selectedType:selectedType];
|
||||
|
||||
JXCategoryIndicatorCellModel *selectedCellModel = (JXCategoryIndicatorCellModel *)self.dataSource[index];
|
||||
selectedCellModel.selectedType = selectedType;
|
||||
for (UIView<JXCategoryIndicatorProtocol> *indicator in self.indicators) {
|
||||
JXCategoryIndicatorParamsModel *indicatorParamsModel = [[JXCategoryIndicatorParamsModel alloc] init];
|
||||
indicatorParamsModel.lastSelectedIndex = lastSelectedIndex;
|
||||
indicatorParamsModel.selectedIndex = index;
|
||||
indicatorParamsModel.selectedCellFrame = clickedCellFrame;
|
||||
indicatorParamsModel.selectedType = selectedType;
|
||||
[indicator jx_selectedCell:indicatorParamsModel];
|
||||
if ([indicator isKindOfClass:[JXCategoryIndicatorBackgroundView class]]) {
|
||||
CGRect maskFrame = indicator.frame;
|
||||
maskFrame.origin.x = maskFrame.origin.x - clickedCellFrame.origin.x;
|
||||
selectedCellModel.backgroundViewMaskFrame = maskFrame;
|
||||
}
|
||||
}
|
||||
|
||||
JXCategoryIndicatorCell *selectedCell = (JXCategoryIndicatorCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
|
||||
[selectedCell reloadData:selectedCellModel];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation JXCategoryIndicatorView (UISubclassingIndicatorHooks)
|
||||
|
||||
- (void)refreshLeftCellModel:(JXCategoryBaseCellModel *)leftCellModel rightCellModel:(JXCategoryBaseCellModel *)rightCellModel ratio:(CGFloat)ratio {
|
||||
if (self.isCellBackgroundColorGradientEnabled) {
|
||||
//处理cell背景色渐变
|
||||
JXCategoryIndicatorCellModel *leftModel = (JXCategoryIndicatorCellModel *)leftCellModel;
|
||||
JXCategoryIndicatorCellModel *rightModel = (JXCategoryIndicatorCellModel *)rightCellModel;
|
||||
if (leftModel.isSelected) {
|
||||
leftModel.cellBackgroundSelectedColor = [JXCategoryFactory interpolationColorFrom:self.cellBackgroundSelectedColor to:self.cellBackgroundUnselectedColor percent:ratio];
|
||||
leftModel.cellBackgroundUnselectedColor = self.cellBackgroundUnselectedColor;
|
||||
}else {
|
||||
leftModel.cellBackgroundUnselectedColor = [JXCategoryFactory interpolationColorFrom:self.cellBackgroundSelectedColor to:self.cellBackgroundUnselectedColor percent:ratio];
|
||||
leftModel.cellBackgroundSelectedColor = self.cellBackgroundSelectedColor;
|
||||
}
|
||||
if (rightModel.isSelected) {
|
||||
rightModel.cellBackgroundSelectedColor = [JXCategoryFactory interpolationColorFrom:self.cellBackgroundUnselectedColor to:self.cellBackgroundSelectedColor percent:ratio];
|
||||
rightModel.cellBackgroundUnselectedColor = self.cellBackgroundUnselectedColor;
|
||||
}else {
|
||||
rightModel.cellBackgroundUnselectedColor = [JXCategoryFactory interpolationColorFrom:self.cellBackgroundUnselectedColor to:self.cellBackgroundSelectedColor percent:ratio];
|
||||
rightModel.cellBackgroundSelectedColor = self.cellBackgroundSelectedColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
23
Pods/JXCategoryView/Sources/JXCategoryView.h
generated
Normal file
23
Pods/JXCategoryView/Sources/JXCategoryView.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
#import "JXCategoryBaseView.h"
|
||||
#import "JXCategoryIndicatorView.h"
|
||||
#import "JXCategoryTitleView.h"
|
||||
#import "JXCategoryImageView.h"
|
||||
#import "JXCategoryTitleImageView.h"
|
||||
#import "JXCategoryNumberView.h"
|
||||
#import "JXCategoryDotView.h"
|
||||
|
||||
#import "JXCategoryFactory.h"
|
||||
#import "JXCategoryIndicatorProtocol.h"
|
||||
#import "JXCategoryViewDefines.h"
|
||||
#import "JXCategoryListContainerView.h"
|
||||
|
||||
#import "JXCategoryIndicatorComponentView.h"
|
||||
#import "JXCategoryIndicatorLineView.h"
|
||||
#import "JXCategoryIndicatorTriangleView.h"
|
||||
#import "JXCategoryIndicatorImageView.h"
|
||||
#import "JXCategoryIndicatorBackgroundView.h"
|
||||
#import "JXCategoryIndicatorBallView.h"
|
||||
#import "JXCategoryIndicatorRainbowLineView.h"
|
||||
|
||||
13
Pods/JXCategoryView/Sources/Number/JXCategoryNumberCell.h
generated
Normal file
13
Pods/JXCategoryView/Sources/Number/JXCategoryNumberCell.h
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryNumberCell.h
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/4/9.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCell.h"
|
||||
|
||||
@interface JXCategoryNumberCell : JXCategoryTitleCell
|
||||
@property (nonatomic, strong) UILabel *numberLabel;
|
||||
@end
|
||||
62
Pods/JXCategoryView/Sources/Number/JXCategoryNumberCell.m
generated
Normal file
62
Pods/JXCategoryView/Sources/Number/JXCategoryNumberCell.m
generated
Normal file
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// JXCategoryNumberCell.m
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/4/9.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryNumberCell.h"
|
||||
#import "JXCategoryNumberCellModel.h"
|
||||
|
||||
@interface JXCategoryNumberCell ()
|
||||
@property (nonatomic, strong) NSLayoutConstraint *numberCenterXConstraint;
|
||||
@property (nonatomic, strong) NSLayoutConstraint *numberCenterYConstraint;
|
||||
@property (nonatomic, strong) NSLayoutConstraint *numberHeightConstraint;
|
||||
@property (nonatomic, strong) NSLayoutConstraint *numberWidthConstraint;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryNumberCell
|
||||
|
||||
- (void)prepareForReuse {
|
||||
[super prepareForReuse];
|
||||
|
||||
self.numberLabel.text = nil;
|
||||
}
|
||||
|
||||
- (void)initializeViews {
|
||||
[super initializeViews];
|
||||
|
||||
self.numberLabel = [[UILabel alloc] init];
|
||||
self.numberLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.numberLabel.layer.masksToBounds = YES;
|
||||
[self.contentView addSubview:self.numberLabel];
|
||||
self.numberLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
self.numberCenterXConstraint = [self.numberLabel.centerXAnchor constraintEqualToAnchor:self.titleLabel.trailingAnchor];
|
||||
self.numberCenterYConstraint = [self.numberLabel.centerYAnchor constraintEqualToAnchor:self.titleLabel.topAnchor];
|
||||
self.numberHeightConstraint = [self.numberLabel.heightAnchor constraintEqualToConstant:0];
|
||||
self.numberWidthConstraint = [self.numberLabel.widthAnchor constraintEqualToConstant:0];
|
||||
[NSLayoutConstraint activateConstraints:@[self.numberCenterXConstraint, self.numberCenterYConstraint, self.numberWidthConstraint, self.numberHeightConstraint]];
|
||||
}
|
||||
|
||||
- (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
|
||||
[super reloadData:cellModel];
|
||||
|
||||
JXCategoryNumberCellModel *myCellModel = (JXCategoryNumberCellModel *)cellModel;
|
||||
self.numberLabel.hidden = (myCellModel.count == 0);
|
||||
self.numberLabel.backgroundColor = myCellModel.numberBackgroundColor;
|
||||
self.numberLabel.font = myCellModel.numberLabelFont;
|
||||
self.numberLabel.textColor = myCellModel.numberTitleColor;
|
||||
self.numberLabel.text = myCellModel.numberString;
|
||||
self.numberLabel.layer.cornerRadius = myCellModel.numberLabelHeight/2.0;
|
||||
self.numberHeightConstraint.constant = myCellModel.numberLabelHeight;
|
||||
self.numberCenterXConstraint.constant = myCellModel.numberLabelOffset.x;
|
||||
self.numberCenterYConstraint.constant = myCellModel.numberLabelOffset.y;
|
||||
if (myCellModel.count < 10 && myCellModel.shouldMakeRoundWhenSingleNumber) {
|
||||
self.numberWidthConstraint.constant = myCellModel.numberLabelHeight;
|
||||
}else {
|
||||
self.numberWidthConstraint.constant = myCellModel.numberStringWidth + myCellModel.numberLabelWidthIncrement;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
25
Pods/JXCategoryView/Sources/Number/JXCategoryNumberCellModel.h
generated
Normal file
25
Pods/JXCategoryView/Sources/Number/JXCategoryNumberCellModel.h
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// JXCategoryNumberCellModel.h
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/4/24.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCellModel.h"
|
||||
|
||||
@interface JXCategoryNumberCellModel : JXCategoryTitleCellModel
|
||||
|
||||
@property (nonatomic, assign) NSInteger count;
|
||||
@property (nonatomic, copy) NSString *numberString;
|
||||
@property (nonatomic, assign, readonly) CGFloat numberStringWidth;
|
||||
@property (nonatomic, copy) void(^numberStringFormatterBlock)(NSInteger number);
|
||||
@property (nonatomic, strong) UIColor *numberBackgroundColor;
|
||||
@property (nonatomic, strong) UIColor *numberTitleColor;
|
||||
@property (nonatomic, assign) CGFloat numberLabelWidthIncrement;
|
||||
@property (nonatomic, assign) CGFloat numberLabelHeight;
|
||||
@property (nonatomic, strong) UIFont *numberLabelFont;
|
||||
@property (nonatomic, assign) CGPoint numberLabelOffset;
|
||||
@property (nonatomic, assign) BOOL shouldMakeRoundWhenSingleNumber;
|
||||
|
||||
@end
|
||||
37
Pods/JXCategoryView/Sources/Number/JXCategoryNumberCellModel.m
generated
Normal file
37
Pods/JXCategoryView/Sources/Number/JXCategoryNumberCellModel.m
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// JXCategoryNumberCellModel.m
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/4/24.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryNumberCellModel.h"
|
||||
|
||||
@implementation JXCategoryNumberCellModel
|
||||
|
||||
- (void)setNumberString:(NSString *)numberString {
|
||||
_numberString = numberString;
|
||||
|
||||
[self updateNumberSizeWidthIfNeeded];
|
||||
}
|
||||
|
||||
- (void)setNumberLabelHeight:(CGFloat)numberLabelHeight {
|
||||
_numberLabelHeight = numberLabelHeight;
|
||||
|
||||
[self updateNumberSizeWidthIfNeeded];
|
||||
}
|
||||
|
||||
- (void)setNumberLabelFont:(UIFont *)numberLabelFont {
|
||||
_numberLabelFont = numberLabelFont;
|
||||
|
||||
[self updateNumberSizeWidthIfNeeded];
|
||||
}
|
||||
|
||||
- (void)updateNumberSizeWidthIfNeeded {
|
||||
if (self.numberLabelFont != nil) {
|
||||
_numberStringWidth = [self.numberString boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, self.numberLabelHeight) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : self.numberLabelFont} context:nil].size.width;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
60
Pods/JXCategoryView/Sources/Number/JXCategoryNumberView.h
generated
Normal file
60
Pods/JXCategoryView/Sources/Number/JXCategoryNumberView.h
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// JXCategoryNumberView.h
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/4/9.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleView.h"
|
||||
#import "JXCategoryNumberCell.h"
|
||||
#import "JXCategoryNumberCellModel.h"
|
||||
|
||||
@interface JXCategoryNumberView : JXCategoryTitleView
|
||||
|
||||
/**
|
||||
需要与titles的count对应
|
||||
*/
|
||||
@property (nonatomic, strong) NSArray <NSNumber *> *counts;
|
||||
|
||||
/**
|
||||
内部默认不会格式化数字,直接转成字符串显示。比如业务需要数字超过999显示999+,可以通过该block实现。
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *(^numberStringFormatterBlock)(NSInteger number);
|
||||
|
||||
/**
|
||||
numberLabel的font,默认:[UIFont systemFontOfSize:11]
|
||||
*/
|
||||
@property (nonatomic, strong) UIFont *numberLabelFont;
|
||||
|
||||
/**
|
||||
数字的背景色,默认:[UIColor colorWithRed:241/255.0 green:147/255.0 blue:95/255.0 alpha:1]
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *numberBackgroundColor;
|
||||
|
||||
/**
|
||||
数字的title颜色,默认:[UIColor whiteColor]
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *numberTitleColor;
|
||||
|
||||
/**
|
||||
numberLabel的宽度补偿,label真实的宽度是文字内容的宽度加上补偿的宽度,默认:10
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat numberLabelWidthIncrement;
|
||||
|
||||
/**
|
||||
numberLabel的高度,默认:14
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat numberLabelHeight;
|
||||
|
||||
/**
|
||||
numberLabel x,y方向的偏移 (+值:水平方向向右,竖直方向向下)
|
||||
*/
|
||||
@property (nonatomic, assign) CGPoint numberLabelOffset;
|
||||
|
||||
/**
|
||||
当是单一数字时,是否让numberLabel变成圆。即numberLabel的宽度等于高度,cornerRadius等于高度/2。当为true,单一数字时会忽略numberLabelWidthIncrement属性。默认为NO;
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL shouldMakeRoundWhenSingleNumber;
|
||||
|
||||
@end
|
||||
61
Pods/JXCategoryView/Sources/Number/JXCategoryNumberView.m
generated
Normal file
61
Pods/JXCategoryView/Sources/Number/JXCategoryNumberView.m
generated
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// JXCategoryNumberView.m
|
||||
// DQGuess
|
||||
//
|
||||
// Created by jiaxin on 2018/4/9.
|
||||
// Copyright © 2018年 jingbo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryNumberView.h"
|
||||
|
||||
@implementation JXCategoryNumberView
|
||||
|
||||
- (void)dealloc {
|
||||
self.numberStringFormatterBlock = nil;
|
||||
}
|
||||
|
||||
- (void)initializeData {
|
||||
[super initializeData];
|
||||
|
||||
self.cellSpacing = 25;
|
||||
_numberTitleColor = [UIColor whiteColor];
|
||||
_numberBackgroundColor = [UIColor colorWithRed:241/255.0 green:147/255.0 blue:95/255.0 alpha:1];
|
||||
_numberLabelHeight = 14;
|
||||
_numberLabelWidthIncrement = 10;
|
||||
_numberLabelFont = [UIFont systemFontOfSize:11];
|
||||
_shouldMakeRoundWhenSingleNumber = NO;
|
||||
}
|
||||
|
||||
- (Class)preferredCellClass {
|
||||
return [JXCategoryNumberCell class];
|
||||
}
|
||||
|
||||
- (void)refreshDataSource {
|
||||
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:self.titles.count];
|
||||
for (int i = 0; i < self.titles.count; i++) {
|
||||
JXCategoryNumberCellModel *cellModel = [[JXCategoryNumberCellModel alloc] init];
|
||||
[tempArray addObject:cellModel];
|
||||
}
|
||||
self.dataSource = [NSArray arrayWithArray:tempArray];
|
||||
}
|
||||
|
||||
- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index {
|
||||
[super refreshCellModel:cellModel index:index];
|
||||
|
||||
JXCategoryNumberCellModel *myCellModel = (JXCategoryNumberCellModel *)cellModel;
|
||||
myCellModel.count = [self.counts[index] integerValue];
|
||||
if (self.numberStringFormatterBlock != nil) {
|
||||
myCellModel.numberString = self.numberStringFormatterBlock(myCellModel.count);
|
||||
}else {
|
||||
myCellModel.numberString = [NSString stringWithFormat:@"%ld", (long)myCellModel.count];
|
||||
}
|
||||
myCellModel.numberBackgroundColor = self.numberBackgroundColor;
|
||||
myCellModel.numberTitleColor = self.numberTitleColor;
|
||||
myCellModel.numberLabelHeight = self.numberLabelHeight;
|
||||
myCellModel.numberLabelOffset = self.numberLabelOffset;
|
||||
myCellModel.numberLabelWidthIncrement = self.numberLabelWidthIncrement;
|
||||
myCellModel.numberLabelFont = self.numberLabelFont;
|
||||
myCellModel.shouldMakeRoundWhenSingleNumber = self.shouldMakeRoundWhenSingleNumber;
|
||||
}
|
||||
|
||||
@end
|
||||
14
Pods/JXCategoryView/Sources/PrivacyInfo.xcprivacy
generated
Normal file
14
Pods/JXCategoryView/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>
|
||||
20
Pods/JXCategoryView/Sources/RLTManager/RTLManager.h
generated
Normal file
20
Pods/JXCategoryView/Sources/RLTManager/RTLManager.h
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// RTLManager.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2020/7/3.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RTLManager : NSObject
|
||||
|
||||
+ (BOOL)supportRTL;
|
||||
+ (void)horizontalFlipView:(UIView *)view;
|
||||
+ (void)horizontalFlipViewIfNeeded:(UIView *)view;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
26
Pods/JXCategoryView/Sources/RLTManager/RTLManager.m
generated
Normal file
26
Pods/JXCategoryView/Sources/RLTManager/RTLManager.m
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// RTLManager.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2020/7/3.
|
||||
//
|
||||
|
||||
#import "RTLManager.h"
|
||||
|
||||
@implementation RTLManager
|
||||
|
||||
+ (BOOL)supportRTL {
|
||||
return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:UIView.appearance.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
|
||||
}
|
||||
|
||||
+ (void)horizontalFlipView:(UIView *)view {
|
||||
view.transform = CGAffineTransformMakeScale(-1, 1);
|
||||
}
|
||||
|
||||
+ (void)horizontalFlipViewIfNeeded:(UIView *)view {
|
||||
if ([self supportRTL]) {
|
||||
[self horizontalFlipView:view];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
27
Pods/JXCategoryView/Sources/Title/JXCategoryTitleCell.h
generated
Normal file
27
Pods/JXCategoryView/Sources/Title/JXCategoryTitleCell.h
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// JXCategoryTitleCell.h
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorCell.h"
|
||||
#import "JXCategoryViewDefines.h"
|
||||
@class JXCategoryTitleCellModel;
|
||||
|
||||
@interface JXCategoryTitleCell : JXCategoryIndicatorCell
|
||||
|
||||
@property (nonatomic, strong) UILabel *titleLabel;
|
||||
@property (nonatomic, strong) UILabel *maskTitleLabel;
|
||||
@property (nonatomic, strong) NSLayoutConstraint *titleLabelCenterX;
|
||||
@property (nonatomic, strong) NSLayoutConstraint *titleLabelCenterY;
|
||||
@property (nonatomic, strong) NSLayoutConstraint *maskTitleLabelCenterX;
|
||||
|
||||
- (JXCategoryCellSelectedAnimationBlock)preferredTitleZoomAnimationBlock:(JXCategoryTitleCellModel *)cellModel baseScale:(CGFloat)baseScale;
|
||||
|
||||
- (JXCategoryCellSelectedAnimationBlock)preferredTitleStrokeWidthAnimationBlock:(JXCategoryTitleCellModel *)cellModel attributedString:(NSMutableAttributedString *)attributedString;
|
||||
|
||||
- (JXCategoryCellSelectedAnimationBlock)preferredTitleColorAnimationBlock:(JXCategoryTitleCellModel *)cellModel;
|
||||
|
||||
@end
|
||||
240
Pods/JXCategoryView/Sources/Title/JXCategoryTitleCell.m
generated
Normal file
240
Pods/JXCategoryView/Sources/Title/JXCategoryTitleCell.m
generated
Normal file
@@ -0,0 +1,240 @@
|
||||
//
|
||||
// JXCategoryTitleCell.m
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCell.h"
|
||||
#import "JXCategoryTitleCellModel.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
#import "RTLManager.h"
|
||||
|
||||
@interface JXCategoryTitleCell ()
|
||||
@property (nonatomic, strong) CALayer *titleMaskLayer;
|
||||
@property (nonatomic, strong) CALayer *maskTitleMaskLayer;
|
||||
@property (nonatomic, strong) NSLayoutConstraint *maskTitleLabelCenterY;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryTitleCell
|
||||
|
||||
- (void)initializeViews {
|
||||
[super initializeViews];
|
||||
|
||||
self.isAccessibilityElement = true;
|
||||
self.accessibilityTraits = UIAccessibilityTraitButton;
|
||||
_titleLabel = [[UILabel alloc] init];
|
||||
self.titleLabel.clipsToBounds = YES;
|
||||
self.titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.contentView addSubview:self.titleLabel];
|
||||
|
||||
self.titleLabelCenterX = [self.titleLabel.centerXAnchor constraintEqualToAnchor:self.contentView.centerXAnchor constant:0];
|
||||
self.titleLabelCenterY = [self.titleLabel.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor constant:0];
|
||||
|
||||
_titleMaskLayer = [CALayer layer];
|
||||
self.titleMaskLayer.backgroundColor = [UIColor redColor].CGColor;
|
||||
|
||||
_maskTitleLabel = [[UILabel alloc] init];
|
||||
self.maskTitleLabel.hidden = YES;
|
||||
self.maskTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
self.maskTitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.contentView addSubview:self.maskTitleLabel];
|
||||
|
||||
self.maskTitleLabelCenterX = [self.maskTitleLabel.centerXAnchor constraintEqualToAnchor:self.contentView.centerXAnchor];
|
||||
self.maskTitleLabelCenterY = [self.maskTitleLabel.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor];
|
||||
|
||||
_maskTitleMaskLayer = [CALayer layer];
|
||||
self.maskTitleMaskLayer.backgroundColor = [UIColor redColor].CGColor;
|
||||
self.maskTitleLabel.layer.mask = self.maskTitleMaskLayer;
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[self.titleLabelCenterX, self.titleLabelCenterY, self.maskTitleLabelCenterX, self.maskTitleLabelCenterY]];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
JXCategoryTitleCellModel *myCellModel = (JXCategoryTitleCellModel *)self.cellModel;
|
||||
switch (myCellModel.titleLabelAnchorPointStyle) {
|
||||
case JXCategoryTitleLabelAnchorPointStyleCenter: {
|
||||
self.titleLabel.layer.anchorPoint = CGPointMake(0.5, 0.5);
|
||||
self.maskTitleLabel.layer.anchorPoint = CGPointMake(0.5, 0.5);
|
||||
self.titleLabelCenterY.constant = 0 + myCellModel.titleLabelVerticalOffset;
|
||||
break;
|
||||
}
|
||||
case JXCategoryTitleLabelAnchorPointStyleTop: {
|
||||
self.titleLabel.layer.anchorPoint = CGPointMake(0.5, 0);
|
||||
self.maskTitleLabel.layer.anchorPoint = CGPointMake(0.5, 0);
|
||||
CGFloat percent = (myCellModel.titleLabelCurrentZoomScale - myCellModel.titleLabelNormalZoomScale)/(myCellModel.titleLabelSelectedZoomScale - myCellModel.titleLabelNormalZoomScale);
|
||||
self.titleLabelCenterY.constant = -myCellModel.titleHeight/2 - myCellModel.titleLabelVerticalOffset - myCellModel.titleLabelZoomSelectedVerticalOffset*percent;
|
||||
break;
|
||||
}
|
||||
case JXCategoryTitleLabelAnchorPointStyleBottom: {
|
||||
self.titleLabel.layer.anchorPoint = CGPointMake(0.5, 1);
|
||||
self.maskTitleLabel.layer.anchorPoint = CGPointMake(0.5, 1);
|
||||
CGFloat percent = (myCellModel.titleLabelCurrentZoomScale - myCellModel.titleLabelNormalZoomScale)/(myCellModel.titleLabelSelectedZoomScale - myCellModel.titleLabelNormalZoomScale);
|
||||
self.titleLabelCenterY.constant = myCellModel.titleHeight/2 + myCellModel.titleLabelVerticalOffset + myCellModel.titleLabelZoomSelectedVerticalOffset*percent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
|
||||
[super reloadData:cellModel];
|
||||
|
||||
JXCategoryTitleCellModel *myCellModel = (JXCategoryTitleCellModel *)cellModel;
|
||||
self.accessibilityLabel = myCellModel.title;
|
||||
self.titleLabel.numberOfLines = myCellModel.titleNumberOfLines;
|
||||
self.maskTitleLabel.numberOfLines = myCellModel.titleNumberOfLines;
|
||||
|
||||
if (myCellModel.isTitleLabelZoomEnabled) {
|
||||
//先把font设置为缩放的最大值,再缩小到最小值,最后根据当前的titleLabelZoomScale值,进行缩放更新。这样就能避免transform从小到大时字体模糊
|
||||
UIFont *maxScaleFont = [UIFont fontWithDescriptor:myCellModel.titleFont.fontDescriptor size:myCellModel.titleFont.pointSize*myCellModel.titleLabelSelectedZoomScale];
|
||||
CGFloat baseScale = myCellModel.titleFont.lineHeight/maxScaleFont.lineHeight;
|
||||
if (myCellModel.isSelectedAnimationEnabled && [self checkCanStartSelectedAnimation:myCellModel]) {
|
||||
JXCategoryCellSelectedAnimationBlock block = [self preferredTitleZoomAnimationBlock:myCellModel baseScale:baseScale];
|
||||
[self addSelectedAnimationBlock:block];
|
||||
} else {
|
||||
self.titleLabel.font = maxScaleFont;
|
||||
self.maskTitleLabel.font = maxScaleFont;
|
||||
CGAffineTransform currentTransform = CGAffineTransformMakeScale(baseScale*myCellModel.titleLabelCurrentZoomScale, baseScale*myCellModel.titleLabelCurrentZoomScale);
|
||||
self.titleLabel.transform = currentTransform;
|
||||
self.maskTitleLabel.transform = currentTransform;
|
||||
}
|
||||
} else {
|
||||
if (myCellModel.isSelected) {
|
||||
self.titleLabel.font = myCellModel.titleSelectedFont;
|
||||
self.maskTitleLabel.font = myCellModel.titleSelectedFont;
|
||||
} else {
|
||||
self.titleLabel.font = myCellModel.titleFont;
|
||||
self.maskTitleLabel.font = myCellModel.titleFont;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *titleString = myCellModel.title ? myCellModel.title : @"";
|
||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:titleString];
|
||||
if (myCellModel.isTitleLabelStrokeWidthEnabled) {
|
||||
if (myCellModel.isSelectedAnimationEnabled && [self checkCanStartSelectedAnimation:myCellModel]) {
|
||||
JXCategoryCellSelectedAnimationBlock block = [self preferredTitleStrokeWidthAnimationBlock:myCellModel attributedString:attributedString];
|
||||
[self addSelectedAnimationBlock:block];
|
||||
} else {
|
||||
[attributedString addAttribute:NSStrokeWidthAttributeName value:@(myCellModel.titleLabelCurrentStrokeWidth) range:NSMakeRange(0, titleString.length)];
|
||||
self.titleLabel.attributedText = attributedString;
|
||||
self.maskTitleLabel.attributedText = attributedString;
|
||||
}
|
||||
} else {
|
||||
self.titleLabel.attributedText = attributedString;
|
||||
self.maskTitleLabel.attributedText = attributedString;
|
||||
}
|
||||
|
||||
if (myCellModel.isTitleLabelMaskEnabled) {
|
||||
self.maskTitleLabel.hidden = NO;
|
||||
self.titleLabel.textColor = myCellModel.titleNormalColor;
|
||||
self.maskTitleLabel.textColor = myCellModel.titleSelectedColor;
|
||||
[self.contentView setNeedsLayout];
|
||||
[self.contentView layoutIfNeeded];
|
||||
|
||||
CGRect topMaskframe = myCellModel.backgroundViewMaskFrame;
|
||||
//将相对于cell的backgroundViewMaskFrame转换为相对于maskTitleLabel
|
||||
//使用self.bounds.size.width而不是self.contentView.bounds.size.width。因为某些情况下,会出现self.bounds是正确的,而self.contentView.bounds还是重用前的状态。
|
||||
topMaskframe.origin.y = 0;
|
||||
CGRect bottomMaskFrame = topMaskframe;
|
||||
CGFloat maskStartX = 0;
|
||||
if (self.maskTitleLabel.bounds.size.width >= self.bounds.size.width) {
|
||||
topMaskframe.origin.x -= (self.maskTitleLabel.bounds.size.width -self.bounds.size.width)/2;
|
||||
bottomMaskFrame.size.width = self.maskTitleLabel.bounds.size.width;
|
||||
maskStartX = -(self.maskTitleLabel.bounds.size.width -self.bounds.size.width)/2;
|
||||
} else {
|
||||
bottomMaskFrame.size.width = self.bounds.size.width;
|
||||
topMaskframe.origin.x -= (self.bounds.size.width -self.maskTitleLabel.bounds.size.width)/2;
|
||||
maskStartX = 0;
|
||||
}
|
||||
bottomMaskFrame.origin.x = topMaskframe.origin.x;
|
||||
if (topMaskframe.origin.x > maskStartX) {
|
||||
bottomMaskFrame.origin.x = topMaskframe.origin.x - bottomMaskFrame.size.width;
|
||||
} else {
|
||||
bottomMaskFrame.origin.x = CGRectGetMaxX(topMaskframe);
|
||||
}
|
||||
|
||||
// 适配RTL布局(镜像x值)
|
||||
if ([RTLManager supportRTL]) {
|
||||
topMaskframe.origin.x = self.maskTitleMaskLayer.superlayer.frame.size.width - CGRectGetMaxX(topMaskframe);
|
||||
bottomMaskFrame.origin.x = self.titleMaskLayer.superlayer.frame.size.width - CGRectGetMaxX(bottomMaskFrame);
|
||||
}
|
||||
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
if (topMaskframe.size.width > 0 && CGRectIntersectsRect(topMaskframe, self.maskTitleLabel.frame)) {
|
||||
self.titleLabel.layer.mask = self.titleMaskLayer;
|
||||
self.maskTitleMaskLayer.frame = topMaskframe;
|
||||
self.titleMaskLayer.frame = bottomMaskFrame;
|
||||
} else {
|
||||
self.maskTitleMaskLayer.frame = topMaskframe;
|
||||
self.titleLabel.layer.mask = nil;
|
||||
}
|
||||
[CATransaction commit];
|
||||
} else {
|
||||
self.maskTitleLabel.hidden = YES;
|
||||
self.titleLabel.layer.mask = nil;
|
||||
if (myCellModel.isSelectedAnimationEnabled && [self checkCanStartSelectedAnimation:myCellModel]) {
|
||||
JXCategoryCellSelectedAnimationBlock block = [self preferredTitleColorAnimationBlock:myCellModel];
|
||||
[self addSelectedAnimationBlock:block];
|
||||
} else {
|
||||
self.titleLabel.textColor = myCellModel.titleCurrentColor;
|
||||
}
|
||||
}
|
||||
|
||||
[self startSelectedAnimationIfNeeded:myCellModel];
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (JXCategoryCellSelectedAnimationBlock)preferredTitleZoomAnimationBlock:(JXCategoryTitleCellModel *)cellModel baseScale:(CGFloat)baseScale {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
return ^(CGFloat percent) {
|
||||
if (cellModel.isSelected) {
|
||||
//将要选中,scale从小到大插值渐变
|
||||
cellModel.titleLabelCurrentZoomScale = [JXCategoryFactory interpolationFrom:cellModel.titleLabelNormalZoomScale to:cellModel.titleLabelSelectedZoomScale percent:percent];
|
||||
} else {
|
||||
//将要取消选中,scale从大到小插值渐变
|
||||
cellModel.titleLabelCurrentZoomScale = [JXCategoryFactory interpolationFrom:cellModel.titleLabelSelectedZoomScale to:cellModel.titleLabelNormalZoomScale percent:percent];
|
||||
}
|
||||
CGAffineTransform currentTransform = CGAffineTransformMakeScale(baseScale*cellModel.titleLabelCurrentZoomScale, baseScale*cellModel.titleLabelCurrentZoomScale);
|
||||
weakSelf.titleLabel.transform = currentTransform;
|
||||
weakSelf.maskTitleLabel.transform = currentTransform;
|
||||
};
|
||||
}
|
||||
|
||||
- (JXCategoryCellSelectedAnimationBlock)preferredTitleStrokeWidthAnimationBlock:(JXCategoryTitleCellModel *)cellModel attributedString:(NSMutableAttributedString *)attributedString {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
return ^(CGFloat percent) {
|
||||
if (cellModel.isSelected) {
|
||||
//将要选中,StrokeWidth从小到大插值渐变
|
||||
cellModel.titleLabelCurrentStrokeWidth = [JXCategoryFactory interpolationFrom:cellModel.titleLabelNormalStrokeWidth to:cellModel.titleLabelSelectedStrokeWidth percent:percent];
|
||||
} else {
|
||||
//将要取消选中,StrokeWidth从大到小插值渐变
|
||||
cellModel.titleLabelCurrentStrokeWidth = [JXCategoryFactory interpolationFrom:cellModel.titleLabelSelectedStrokeWidth to:cellModel.titleLabelNormalStrokeWidth percent:percent];
|
||||
}
|
||||
[attributedString addAttribute:NSStrokeWidthAttributeName value:@(cellModel.titleLabelCurrentStrokeWidth) range:NSMakeRange(0, attributedString.string.length)];
|
||||
weakSelf.titleLabel.attributedText = attributedString;
|
||||
weakSelf.maskTitleLabel.attributedText = attributedString;
|
||||
};
|
||||
}
|
||||
|
||||
- (JXCategoryCellSelectedAnimationBlock)preferredTitleColorAnimationBlock:(JXCategoryTitleCellModel *)cellModel {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
return ^(CGFloat percent) {
|
||||
if (cellModel.isSelected) {
|
||||
//将要选中,textColor从titleNormalColor到titleSelectedColor插值渐变
|
||||
cellModel.titleCurrentColor = [JXCategoryFactory interpolationColorFrom:cellModel.titleNormalColor to:cellModel.titleSelectedColor percent:percent];
|
||||
} else {
|
||||
//将要取消选中,textColor从titleSelectedColor到titleNormalColor插值渐变
|
||||
cellModel.titleCurrentColor = [JXCategoryFactory interpolationColorFrom:cellModel.titleSelectedColor to:cellModel.titleNormalColor percent:percent];
|
||||
}
|
||||
weakSelf.titleLabel.textColor = cellModel.titleCurrentColor;
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
44
Pods/JXCategoryView/Sources/Title/JXCategoryTitleCellModel.h
generated
Normal file
44
Pods/JXCategoryView/Sources/Title/JXCategoryTitleCellModel.h
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// JXCategoryTitleCellModel.h
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorCellModel.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JXCategoryViewDefines.h"
|
||||
|
||||
@interface JXCategoryTitleCellModel : JXCategoryIndicatorCellModel
|
||||
|
||||
@property (nonatomic, copy) NSString *title;
|
||||
|
||||
@property (nonatomic, assign, readonly) CGFloat titleHeight;
|
||||
|
||||
@property (nonatomic, assign) NSInteger titleNumberOfLines;
|
||||
@property (nonatomic, assign) CGFloat titleLabelVerticalOffset;
|
||||
|
||||
@property (nonatomic, strong) UIColor *titleNormalColor;
|
||||
@property (nonatomic, strong) UIColor *titleCurrentColor;
|
||||
@property (nonatomic, strong) UIColor *titleSelectedColor;
|
||||
|
||||
@property (nonatomic, strong) UIFont *titleFont;
|
||||
@property (nonatomic, strong) UIFont *titleSelectedFont;
|
||||
|
||||
@property (nonatomic, assign, getter=isTitleLabelMaskEnabled) BOOL titleLabelMaskEnabled;
|
||||
@property (nonatomic, assign, getter=isTitleLabelZoomEnabled) BOOL titleLabelZoomEnabled;
|
||||
|
||||
@property (nonatomic, assign) CGFloat titleLabelNormalZoomScale;
|
||||
@property (nonatomic, assign) CGFloat titleLabelCurrentZoomScale;
|
||||
@property (nonatomic, assign) CGFloat titleLabelSelectedZoomScale;
|
||||
@property (nonatomic, assign) CGFloat titleLabelZoomSelectedVerticalOffset;
|
||||
|
||||
@property (nonatomic, assign, getter=isTitleLabelStrokeWidthEnabled) BOOL titleLabelStrokeWidthEnabled;
|
||||
@property (nonatomic, assign) CGFloat titleLabelNormalStrokeWidth;
|
||||
@property (nonatomic, assign) CGFloat titleLabelCurrentStrokeWidth;
|
||||
@property (nonatomic, assign) CGFloat titleLabelSelectedStrokeWidth;
|
||||
|
||||
@property (nonatomic, assign) JXCategoryTitleLabelAnchorPointStyle titleLabelAnchorPointStyle;
|
||||
|
||||
@end
|
||||
31
Pods/JXCategoryView/Sources/Title/JXCategoryTitleCellModel.m
generated
Normal file
31
Pods/JXCategoryView/Sources/Title/JXCategoryTitleCellModel.m
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// JXCategoryTitleCellModel.m
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCellModel.h"
|
||||
|
||||
@implementation JXCategoryTitleCellModel
|
||||
|
||||
- (void)setTitle:(NSString *)title {
|
||||
_title = title;
|
||||
|
||||
[self updateNumberSizeWidthIfNeeded];
|
||||
}
|
||||
|
||||
- (void)setTitleFont:(UIFont *)titleFont {
|
||||
_titleFont = titleFont;
|
||||
|
||||
[self updateNumberSizeWidthIfNeeded];
|
||||
}
|
||||
|
||||
- (void)updateNumberSizeWidthIfNeeded {
|
||||
if (self.titleFont) {
|
||||
_titleHeight = [self.title boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : self.titleFont} context:nil].size.height;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
63
Pods/JXCategoryView/Sources/Title/JXCategoryTitleView.h
generated
Normal file
63
Pods/JXCategoryView/Sources/Title/JXCategoryTitleView.h
generated
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// JXCategoryView.h
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryIndicatorView.h"
|
||||
#import "JXCategoryTitleCell.h"
|
||||
#import "JXCategoryTitleCellModel.h"
|
||||
#import "JXCategoryViewDefines.h"
|
||||
|
||||
@class JXCategoryTitleView;
|
||||
|
||||
@protocol JXCategoryTitleViewDataSource <NSObject>
|
||||
@optional
|
||||
// 如果将JXCategoryTitleView嵌套进UITableView的cell,每次重用的时候,JXCategoryTitleView进行reloadData时,会重新计算所有的title宽度。所以该应用场景,需要UITableView的cellModel缓存titles的文字宽度,再通过该代理方法返回给JXCategoryTitleView。
|
||||
// 如果实现了该方法就以该方法返回的宽度为准,不触发内部默认的文字宽度计算。
|
||||
- (CGFloat)categoryTitleView:(JXCategoryTitleView *)titleView widthForTitle:(NSString *)title;
|
||||
@end
|
||||
|
||||
|
||||
@interface JXCategoryTitleView : JXCategoryIndicatorView
|
||||
|
||||
@property (nonatomic, weak) id<JXCategoryTitleViewDataSource> titleDataSource;
|
||||
|
||||
@property (nonatomic, strong) NSArray <NSString *>*titles;
|
||||
|
||||
@property (nonatomic, assign) NSInteger titleNumberOfLines; //默认:1
|
||||
|
||||
@property (nonatomic, strong) UIColor *titleColor; //默认:[UIColor blackColor]
|
||||
|
||||
@property (nonatomic, strong) UIColor *titleSelectedColor; //默认:[UIColor redColor]
|
||||
|
||||
@property (nonatomic, strong) UIFont *titleFont; //默认:[UIFont systemFontOfSize:15]
|
||||
|
||||
@property (nonatomic, strong) UIFont *titleSelectedFont; //文字被选中的字体。默认:与titleFont一样
|
||||
|
||||
@property (nonatomic, assign, getter=isTitleColorGradientEnabled) BOOL titleColorGradientEnabled; //默认:NO,title的颜色是否渐变过渡
|
||||
|
||||
@property (nonatomic, assign, getter=isTitleLabelMaskEnabled) BOOL titleLabelMaskEnabled; //默认:NO,titleLabel是否遮罩过滤。
|
||||
|
||||
//----------------------titleLabelZoomEnabled-----------------------//
|
||||
@property (nonatomic, assign, getter=isTitleLabelZoomEnabled) BOOL titleLabelZoomEnabled; //默认为NO。为YES时titleSelectedFont失效,以titleFont为准。
|
||||
|
||||
@property (nonatomic, assign, getter=isTitleLabelZoomScrollGradientEnabled) BOOL titleLabelZoomScrollGradientEnabled; //手势滚动中,是否需要更新状态。默认为YES
|
||||
|
||||
@property (nonatomic, assign) CGFloat titleLabelZoomScale; //默认1.2,titleLabelZoomEnabled为YES才生效。是对字号的缩放,比如titleFont的pointSize为10,放大之后字号就是10*1.2=12。
|
||||
|
||||
@property (nonatomic, assign) CGFloat titleLabelZoomSelectedVerticalOffset; //titleLabelZoomEnabled设置为YES,会对titleLabel进行transform缩放,当titleLabelZoomScale过大时(比如设置为2),选中的文本被放大之后底部会有很大的空白,从视觉上看就跟其他未选中的文本不在一个水平线上。这个时候就可以用这个值进行调整。
|
||||
|
||||
//----------------------titleLabelStrokeWidth-----------------------//
|
||||
@property (nonatomic, assign, getter=isTitleLabelStrokeWidthEnabled) BOOL titleLabelStrokeWidthEnabled; //默认:NO
|
||||
|
||||
@property (nonatomic, assign) CGFloat titleLabelSelectedStrokeWidth; //默认:-3,用于控制字体的粗细(底层通过NSStrokeWidthAttributeName实现)。使用该属性,务必让titleFont和titleSelectedFont设置为一样的!!!
|
||||
|
||||
//----------------------titleLabel缩放中心位置-----------------------//
|
||||
@property (nonatomic, assign) CGFloat titleLabelVerticalOffset; //titleLabel锚点垂直方向的位置偏移,数值越大越偏离中心,默认为:0
|
||||
|
||||
@property (nonatomic, assign) JXCategoryTitleLabelAnchorPointStyle titleLabelAnchorPointStyle; //titleLabel锚点位置,用于调整titleLabel缩放时的基准位置。默认为:JXCategoryTitleLabelAnchorPointStyleCenter
|
||||
|
||||
@end
|
||||
164
Pods/JXCategoryView/Sources/Title/JXCategoryTitleView.m
generated
Normal file
164
Pods/JXCategoryView/Sources/Title/JXCategoryTitleView.m
generated
Normal file
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// JXCategoryView.m
|
||||
// UI系列测试
|
||||
//
|
||||
// Created by jiaxin on 2018/3/15.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleView.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@implementation JXCategoryTitleView
|
||||
|
||||
- (void)initializeData {
|
||||
[super initializeData];
|
||||
|
||||
_titleNumberOfLines = 1;
|
||||
_titleLabelZoomEnabled = NO;
|
||||
_titleLabelZoomScale = 1.2;
|
||||
_titleColor = [UIColor blackColor];
|
||||
_titleSelectedColor = [UIColor redColor];
|
||||
_titleFont = [UIFont systemFontOfSize:15];
|
||||
_titleColorGradientEnabled = NO;
|
||||
_titleLabelMaskEnabled = NO;
|
||||
_titleLabelZoomScrollGradientEnabled = YES;
|
||||
_titleLabelStrokeWidthEnabled = NO;
|
||||
_titleLabelSelectedStrokeWidth = -3;
|
||||
_titleLabelVerticalOffset = 0;
|
||||
_titleLabelAnchorPointStyle = JXCategoryTitleLabelAnchorPointStyleCenter;
|
||||
}
|
||||
|
||||
- (UIFont *)titleSelectedFont {
|
||||
if (_titleSelectedFont) {
|
||||
return _titleSelectedFont;
|
||||
}
|
||||
return self.titleFont;
|
||||
}
|
||||
|
||||
#pragma mark - Override
|
||||
|
||||
- (Class)preferredCellClass {
|
||||
return [JXCategoryTitleCell class];
|
||||
}
|
||||
|
||||
- (void)refreshDataSource {
|
||||
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:self.titles.count];
|
||||
for (int i = 0; i < self.titles.count; i++) {
|
||||
JXCategoryTitleCellModel *cellModel = [[JXCategoryTitleCellModel alloc] init];
|
||||
[tempArray addObject:cellModel];
|
||||
}
|
||||
self.dataSource = [NSArray arrayWithArray:tempArray];
|
||||
}
|
||||
|
||||
- (void)refreshSelectedCellModel:(JXCategoryBaseCellModel *)selectedCellModel unselectedCellModel:(JXCategoryBaseCellModel *)unselectedCellModel {
|
||||
[super refreshSelectedCellModel:selectedCellModel unselectedCellModel:unselectedCellModel];
|
||||
|
||||
JXCategoryTitleCellModel *myUnselectedCellModel = (JXCategoryTitleCellModel *)unselectedCellModel;
|
||||
JXCategoryTitleCellModel *myselectedCellModel = (JXCategoryTitleCellModel *)selectedCellModel;
|
||||
if (self.isSelectedAnimationEnabled && (selectedCellModel.selectedType == JXCategoryCellSelectedTypeClick || selectedCellModel.selectedType == JXCategoryCellSelectedTypeCode)) {
|
||||
//开启了动画过渡,且cell在屏幕内,current的属性值会在cell里面进行动画插值更新
|
||||
//1、当unselectedCell在屏幕外的时候,还是需要在这里更新值
|
||||
//2、当selectedCell在屏幕外的时候,还是需要在这里更新值(比如调用selectItemAtIndex方法选中的时候)
|
||||
BOOL isUnselectedCellVisible = NO;
|
||||
BOOL isSelectedCellVisible = NO;
|
||||
NSArray *indexPaths = [self.collectionView indexPathsForVisibleItems];
|
||||
for (NSIndexPath *indexPath in indexPaths) {
|
||||
if (indexPath.item == myUnselectedCellModel.index) {
|
||||
isUnselectedCellVisible = YES;
|
||||
continue;
|
||||
} else if (indexPath.item == myselectedCellModel.index) {
|
||||
isSelectedCellVisible = YES;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!isUnselectedCellVisible) {
|
||||
//但是当unselectedCell在屏幕外时,不会在cell里面通过动画插值更新,在这里直接更新
|
||||
myUnselectedCellModel.titleCurrentColor = myUnselectedCellModel.titleNormalColor;
|
||||
myUnselectedCellModel.titleLabelCurrentZoomScale = myUnselectedCellModel.titleLabelNormalZoomScale;
|
||||
myUnselectedCellModel.titleLabelCurrentStrokeWidth = myUnselectedCellModel.titleLabelNormalStrokeWidth;
|
||||
}
|
||||
if (!isSelectedCellVisible) {
|
||||
//但是当selectedCell在屏幕外时,不会在cell里面通过动画插值更新,在这里直接更新
|
||||
myselectedCellModel.titleCurrentColor = myselectedCellModel.titleSelectedColor;
|
||||
myselectedCellModel.titleLabelCurrentZoomScale = myselectedCellModel.titleLabelSelectedZoomScale;
|
||||
myselectedCellModel.titleLabelCurrentStrokeWidth = myselectedCellModel.titleLabelSelectedStrokeWidth;
|
||||
}
|
||||
} else {
|
||||
//没有开启动画,可以直接更新属性
|
||||
myselectedCellModel.titleCurrentColor = myselectedCellModel.titleSelectedColor;
|
||||
myselectedCellModel.titleLabelCurrentZoomScale = myselectedCellModel.titleLabelSelectedZoomScale;
|
||||
myselectedCellModel.titleLabelCurrentStrokeWidth = myselectedCellModel.titleLabelSelectedStrokeWidth;
|
||||
|
||||
myUnselectedCellModel.titleCurrentColor = myUnselectedCellModel.titleNormalColor;
|
||||
myUnselectedCellModel.titleLabelCurrentZoomScale = myUnselectedCellModel.titleLabelNormalZoomScale;
|
||||
myUnselectedCellModel.titleLabelCurrentStrokeWidth = myUnselectedCellModel.titleLabelNormalStrokeWidth;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshLeftCellModel:(JXCategoryBaseCellModel *)leftCellModel rightCellModel:(JXCategoryBaseCellModel *)rightCellModel ratio:(CGFloat)ratio {
|
||||
[super refreshLeftCellModel:leftCellModel rightCellModel:rightCellModel ratio:ratio];
|
||||
|
||||
JXCategoryTitleCellModel *leftModel = (JXCategoryTitleCellModel *)leftCellModel;
|
||||
JXCategoryTitleCellModel *rightModel = (JXCategoryTitleCellModel *)rightCellModel;
|
||||
|
||||
if (self.isTitleLabelZoomEnabled && self.isTitleLabelZoomScrollGradientEnabled) {
|
||||
leftModel.titleLabelCurrentZoomScale = [JXCategoryFactory interpolationFrom:self.titleLabelZoomScale to:1.0 percent:ratio];
|
||||
rightModel.titleLabelCurrentZoomScale = [JXCategoryFactory interpolationFrom:1.0 to:self.titleLabelZoomScale percent:ratio];
|
||||
}
|
||||
|
||||
if (self.isTitleLabelStrokeWidthEnabled) {
|
||||
leftModel.titleLabelCurrentStrokeWidth = [JXCategoryFactory interpolationFrom:leftModel.titleLabelSelectedStrokeWidth to:leftModel.titleLabelNormalStrokeWidth percent:ratio];
|
||||
rightModel.titleLabelCurrentStrokeWidth = [JXCategoryFactory interpolationFrom:rightModel.titleLabelNormalStrokeWidth to:rightModel.titleLabelSelectedStrokeWidth percent:ratio];
|
||||
}
|
||||
|
||||
if (self.isTitleColorGradientEnabled) {
|
||||
leftModel.titleCurrentColor = [JXCategoryFactory interpolationColorFrom:self.titleSelectedColor to:self.titleColor percent:ratio];
|
||||
rightModel.titleCurrentColor = [JXCategoryFactory interpolationColorFrom:self.titleColor to:self.titleSelectedColor percent:ratio];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)preferredCellWidthAtIndex:(NSInteger)index {
|
||||
if (self.cellWidth == JXCategoryViewAutomaticDimension) {
|
||||
if (self.titleDataSource && [self.titleDataSource respondsToSelector:@selector(categoryTitleView:widthForTitle:)]) {
|
||||
return [self.titleDataSource categoryTitleView:self widthForTitle:self.titles[index]];
|
||||
} else {
|
||||
return ceilf([self.titles[index] boundingRectWithSize:CGSizeMake(MAXFLOAT, self.bounds.size.height) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : self.titleFont} context:nil].size.width);
|
||||
}
|
||||
} else {
|
||||
return self.cellWidth;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index {
|
||||
[super refreshCellModel:cellModel index:index];
|
||||
|
||||
JXCategoryTitleCellModel *model = (JXCategoryTitleCellModel *)cellModel;
|
||||
model.title = self.titles[index];
|
||||
model.titleNumberOfLines = self.titleNumberOfLines;
|
||||
model.titleFont = self.titleFont;
|
||||
model.titleSelectedFont = self.titleSelectedFont;
|
||||
model.titleNormalColor = self.titleColor;
|
||||
model.titleSelectedColor = self.titleSelectedColor;
|
||||
model.titleLabelMaskEnabled = self.isTitleLabelMaskEnabled;
|
||||
model.titleLabelZoomEnabled = self.isTitleLabelZoomEnabled;
|
||||
model.titleLabelNormalZoomScale = 1;
|
||||
model.titleLabelZoomSelectedVerticalOffset = self.titleLabelZoomSelectedVerticalOffset;
|
||||
model.titleLabelSelectedZoomScale = self.titleLabelZoomScale;
|
||||
model.titleLabelStrokeWidthEnabled = self.isTitleLabelStrokeWidthEnabled;
|
||||
model.titleLabelNormalStrokeWidth = 0;
|
||||
model.titleLabelSelectedStrokeWidth = self.titleLabelSelectedStrokeWidth;
|
||||
model.titleLabelVerticalOffset = self.titleLabelVerticalOffset;
|
||||
model.titleLabelAnchorPointStyle = self.titleLabelAnchorPointStyle;
|
||||
if (index == self.selectedIndex) {
|
||||
model.titleCurrentColor = model.titleSelectedColor;
|
||||
model.titleLabelCurrentZoomScale = model.titleLabelSelectedZoomScale;
|
||||
model.titleLabelCurrentStrokeWidth= model.titleLabelSelectedStrokeWidth;
|
||||
}else {
|
||||
model.titleCurrentColor = model.titleNormalColor;
|
||||
model.titleLabelCurrentZoomScale = model.titleLabelNormalZoomScale;
|
||||
model.titleLabelCurrentStrokeWidth = model.titleLabelNormalStrokeWidth;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
15
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageCell.h
generated
Normal file
15
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageCell.h
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// JXCategoryImageCell.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/8.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCell.h"
|
||||
|
||||
@interface JXCategoryTitleImageCell : JXCategoryTitleCell
|
||||
|
||||
@property (nonatomic, strong) UIImageView *imageView;
|
||||
|
||||
@end
|
||||
148
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageCell.m
generated
Normal file
148
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageCell.m
generated
Normal file
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// JXCategoryImageCell.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/8.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleImageCell.h"
|
||||
#import "JXCategoryTitleImageCellModel.h"
|
||||
|
||||
@interface JXCategoryTitleImageCell()
|
||||
@property (nonatomic, strong) id currentImageInfo;
|
||||
@property (nonatomic, strong) NSString *currentImageName;
|
||||
@property (nonatomic, strong) NSURL *currentImageURL;
|
||||
@property (nonatomic, strong) UIStackView *stackView;
|
||||
@property (nonatomic, strong) NSLayoutConstraint *imageViewWidthConstraint;
|
||||
@property (nonatomic, strong) NSLayoutConstraint *imageViewHeightConstraint;
|
||||
@end
|
||||
|
||||
@implementation JXCategoryTitleImageCell
|
||||
|
||||
- (void)prepareForReuse {
|
||||
[super prepareForReuse];
|
||||
|
||||
self.currentImageInfo = nil;
|
||||
self.currentImageName = nil;
|
||||
self.currentImageURL = nil;
|
||||
}
|
||||
|
||||
- (void)initializeViews {
|
||||
[super initializeViews];
|
||||
|
||||
[self.titleLabel removeFromSuperview];
|
||||
|
||||
_imageView = [[UIImageView alloc] init];
|
||||
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
self.imageViewWidthConstraint = [self.imageView.widthAnchor constraintEqualToConstant:0];
|
||||
self.imageViewWidthConstraint.active = YES;
|
||||
self.imageViewHeightConstraint = [self.imageView.heightAnchor constraintEqualToConstant:0];
|
||||
self.imageViewHeightConstraint.active = YES;
|
||||
|
||||
_stackView = [[UIStackView alloc] init];
|
||||
self.stackView.alignment = UIStackViewAlignmentCenter;
|
||||
[self.contentView addSubview:self.stackView];
|
||||
self.stackView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.stackView.centerXAnchor constraintEqualToAnchor:self.contentView.centerXAnchor].active = YES;
|
||||
[self.stackView.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor].active = YES;
|
||||
}
|
||||
|
||||
- (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
|
||||
[super reloadData:cellModel];
|
||||
|
||||
JXCategoryTitleImageCellModel *myCellModel = (JXCategoryTitleImageCellModel *)cellModel;
|
||||
|
||||
self.titleLabel.hidden = NO;
|
||||
self.imageView.hidden = NO;
|
||||
[self.stackView removeArrangedSubview:self.titleLabel];
|
||||
[self.stackView removeArrangedSubview:self.imageView];
|
||||
CGSize imageSize = myCellModel.imageSize;
|
||||
self.imageViewWidthConstraint.constant = imageSize.width;
|
||||
self.imageViewHeightConstraint.constant = imageSize.height;
|
||||
self.stackView.spacing = myCellModel.titleImageSpacing;
|
||||
|
||||
switch (myCellModel.imageType) {
|
||||
case JXCategoryTitleImageType_TopImage: {
|
||||
self.stackView.axis = UILayoutConstraintAxisVertical;
|
||||
[self.stackView addArrangedSubview:self.imageView];
|
||||
[self.stackView addArrangedSubview:self.titleLabel];
|
||||
break;
|
||||
}
|
||||
case JXCategoryTitleImageType_LeftImage: {
|
||||
self.stackView.axis = UILayoutConstraintAxisHorizontal;
|
||||
[self.stackView addArrangedSubview:self.imageView];
|
||||
[self.stackView addArrangedSubview:self.titleLabel];
|
||||
break;
|
||||
}
|
||||
case JXCategoryTitleImageType_BottomImage: {
|
||||
self.stackView.axis = UILayoutConstraintAxisVertical;
|
||||
[self.stackView addArrangedSubview:self.titleLabel];
|
||||
[self.stackView addArrangedSubview:self.imageView];
|
||||
break;
|
||||
}
|
||||
case JXCategoryTitleImageType_RightImage: {
|
||||
self.stackView.axis = UILayoutConstraintAxisHorizontal;
|
||||
[self.stackView addArrangedSubview:self.titleLabel];
|
||||
[self.stackView addArrangedSubview:self.imageView];
|
||||
break;
|
||||
}
|
||||
case JXCategoryTitleImageType_OnlyImage: {
|
||||
self.titleLabel.hidden = YES;
|
||||
[self.stackView addArrangedSubview:self.imageView];
|
||||
break;
|
||||
}
|
||||
case JXCategoryTitleImageType_OnlyTitle: {
|
||||
self.imageView.hidden = YES;
|
||||
[self.stackView addArrangedSubview:self.titleLabel];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//因为`- (void)reloadData:(JXCategoryBaseCellModel *)cellModel`方法会回调多次,尤其是左右滚动的时候会调用无数次,如果每次都触发图片加载,会非常消耗性能。所以只会在图片发生了变化的时候,才进行图片加载。
|
||||
if (myCellModel.loadImageBlock != nil) {
|
||||
id currentImageInfo = myCellModel.imageInfo;
|
||||
if (myCellModel.isSelected) {
|
||||
currentImageInfo = myCellModel.selectedImageInfo;
|
||||
}
|
||||
if (currentImageInfo && ![currentImageInfo isEqual:self.currentImageInfo]) {
|
||||
self.currentImageInfo = currentImageInfo;
|
||||
if (myCellModel.loadImageBlock) {
|
||||
myCellModel.loadImageBlock(self.imageView, currentImageInfo);
|
||||
}
|
||||
}
|
||||
}else {
|
||||
NSString *currentImageName;
|
||||
NSURL *currentImageURL;
|
||||
if (myCellModel.imageName) {
|
||||
currentImageName = myCellModel.imageName;
|
||||
} else if (myCellModel.imageURL) {
|
||||
currentImageURL = myCellModel.imageURL;
|
||||
}
|
||||
if (myCellModel.isSelected) {
|
||||
if (myCellModel.selectedImageName) {
|
||||
currentImageName = myCellModel.selectedImageName;
|
||||
} else if (myCellModel.selectedImageURL) {
|
||||
currentImageURL = myCellModel.selectedImageURL;
|
||||
}
|
||||
}
|
||||
if (currentImageName && ![currentImageName isEqualToString:self.currentImageName]) {
|
||||
self.currentImageName = currentImageName;
|
||||
self.imageView.image = [UIImage imageNamed:currentImageName];
|
||||
} else if (currentImageURL && ![currentImageURL.absoluteString isEqualToString:self.currentImageURL.absoluteString]) {
|
||||
self.currentImageURL = currentImageURL;
|
||||
if (myCellModel.loadImageCallback) {
|
||||
myCellModel.loadImageCallback(self.imageView, currentImageURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (myCellModel.isImageZoomEnabled) {
|
||||
self.imageView.transform = CGAffineTransformMakeScale(myCellModel.imageZoomScale, myCellModel.imageZoomScale);
|
||||
} else {
|
||||
self.imageView.transform = CGAffineTransformIdentity;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
43
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageCellModel.h
generated
Normal file
43
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageCellModel.h
generated
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// JXCategoryTitleImageCellModel.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/8.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCellModel.h"
|
||||
|
||||
typedef NS_ENUM(NSUInteger, JXCategoryTitleImageType) {
|
||||
JXCategoryTitleImageType_TopImage = 0,
|
||||
JXCategoryTitleImageType_LeftImage,
|
||||
JXCategoryTitleImageType_BottomImage,
|
||||
JXCategoryTitleImageType_RightImage,
|
||||
JXCategoryTitleImageType_OnlyImage,
|
||||
JXCategoryTitleImageType_OnlyTitle,
|
||||
};
|
||||
|
||||
@interface JXCategoryTitleImageCellModel : JXCategoryTitleCellModel
|
||||
|
||||
@property (nonatomic, assign) JXCategoryTitleImageType imageType;
|
||||
|
||||
@property (nonatomic, strong) id imageInfo;
|
||||
@property (nonatomic, strong) id selectedImageInfo;
|
||||
@property (nonatomic, copy) void(^loadImageBlock)(UIImageView *imageView, id info);
|
||||
|
||||
@property (nonatomic, assign) CGSize imageSize; //默认CGSizeMake(20, 20)
|
||||
|
||||
@property (nonatomic, assign) CGFloat titleImageSpacing; //titleLabel和ImageView的间距,默认5
|
||||
|
||||
@property (nonatomic, assign, getter=isImageZoomEnabled) BOOL imageZoomEnabled;
|
||||
|
||||
@property (nonatomic, assign) CGFloat imageZoomScale;
|
||||
|
||||
/// 以下属性将会被弃用
|
||||
@property (nonatomic, copy) NSString *imageName; //加载bundle内的图片
|
||||
@property (nonatomic, strong) NSURL *imageURL; //图片URL
|
||||
@property (nonatomic, copy) NSString *selectedImageName;
|
||||
@property (nonatomic, strong) NSURL *selectedImageURL;
|
||||
@property (nonatomic, copy) void(^loadImageCallback)(UIImageView *imageView, NSURL *imageURL);
|
||||
|
||||
@end
|
||||
13
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageCellModel.m
generated
Normal file
13
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageCellModel.m
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryTitleImageCellModel.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/8.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleImageCellModel.h"
|
||||
|
||||
@implementation JXCategoryTitleImageCellModel
|
||||
|
||||
@end
|
||||
42
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageView.h
generated
Normal file
42
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageView.h
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// JXCategoryTitleImageView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/8.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleView.h"
|
||||
#import "JXCategoryTitleImageCell.h"
|
||||
#import "JXCategoryTitleImageCellModel.h"
|
||||
|
||||
@interface JXCategoryTitleImageView : JXCategoryTitleView
|
||||
|
||||
//imageInfo数组可以传入imageName字符串或者image的URL地址等,然后会通过loadImageBlock透传回来,把imageView对于图片的加载过程完全交给使用者决定。
|
||||
@property (nonatomic, strong) NSArray <id>*imageInfoArray;
|
||||
@property (nonatomic, strong) NSArray <id>*selectedImageInfoArray;
|
||||
@property (nonatomic, copy) void(^loadImageBlock)(UIImageView *imageView, id info);
|
||||
//图片尺寸。默认CGSizeMake(20, 20)
|
||||
@property (nonatomic, assign) CGSize imageSize;
|
||||
//titleLabel和ImageView的间距,默认5
|
||||
@property (nonatomic, assign) CGFloat titleImageSpacing;
|
||||
//图片是否缩放。默认为NO
|
||||
@property (nonatomic, assign, getter=isImageZoomEnabled) BOOL imageZoomEnabled;
|
||||
//图片缩放的最大scale。默认1.2,imageZoomEnabled为YES才生效
|
||||
@property (nonatomic, assign) CGFloat imageZoomScale;
|
||||
//默认@[JXCategoryTitleImageType_LeftImage...]
|
||||
@property (nonatomic, strong) NSArray <NSNumber *> *imageTypes;
|
||||
|
||||
//下面的属性将会被弃用,请使用`imageInfoArray`、`selectedImageInfoArray`、`loadImageBlock`属性完成需求。
|
||||
//普通状态下的imageNames,通过[UIImage imageNamed:]方法加载
|
||||
@property (nonatomic, strong) NSArray <NSString *>*imageNames;
|
||||
//选中状态下的imageNames,通过[UIImage imageNamed:]方法加载。可选
|
||||
@property (nonatomic, strong) NSArray <NSString *>*selectedImageNames;
|
||||
//普通状态下的imageURLs,通过loadImageCallback回调加载
|
||||
@property (nonatomic, strong) NSArray <NSURL *>*imageURLs;
|
||||
//选中状态下的selectedImageURLs,通过loadImageCallback回调加载
|
||||
@property (nonatomic, strong) NSArray <NSURL *>*selectedImageURLs;
|
||||
//使用imageURL从远端下载图片进行加载,建议使用SDWebImage等第三方库进行下载。
|
||||
@property (nonatomic, copy) void(^loadImageCallback)(UIImageView *imageView, NSURL *imageURL);
|
||||
|
||||
@end
|
||||
126
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageView.m
generated
Normal file
126
Pods/JXCategoryView/Sources/TitleImage/JXCategoryTitleImageView.m
generated
Normal file
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// JXCategoryTitleImageView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2018/8/8.
|
||||
// Copyright © 2018年 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleImageView.h"
|
||||
#import "JXCategoryTitleImageCell.h"
|
||||
#import "JXCategoryTitleImageCellModel.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@implementation JXCategoryTitleImageView
|
||||
|
||||
- (void)dealloc {
|
||||
self.loadImageBlock = nil;
|
||||
self.loadImageCallback = nil;
|
||||
}
|
||||
|
||||
- (void)initializeData {
|
||||
[super initializeData];
|
||||
|
||||
_imageSize = CGSizeMake(20, 20);
|
||||
_titleImageSpacing = 5;
|
||||
_imageZoomEnabled = NO;
|
||||
_imageZoomScale = 1.2;
|
||||
}
|
||||
|
||||
- (Class)preferredCellClass {
|
||||
return [JXCategoryTitleImageCell class];
|
||||
}
|
||||
|
||||
- (void)refreshDataSource {
|
||||
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:self.titles.count];
|
||||
for (int i = 0; i < self.titles.count; i++) {
|
||||
JXCategoryTitleImageCellModel *cellModel = [[JXCategoryTitleImageCellModel alloc] init];
|
||||
[tempArray addObject:cellModel];
|
||||
}
|
||||
self.dataSource = [NSArray arrayWithArray:tempArray];
|
||||
|
||||
if (!self.imageTypes || (self.imageTypes.count == 0)) {
|
||||
NSMutableArray *types = [NSMutableArray arrayWithCapacity:self.titles.count];
|
||||
for (int i = 0; i< self.titles.count; i++) {
|
||||
[types addObject:@(JXCategoryTitleImageType_LeftImage)];
|
||||
}
|
||||
self.imageTypes = [NSArray arrayWithArray:types];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index {
|
||||
[super refreshCellModel:cellModel index:index];
|
||||
|
||||
JXCategoryTitleImageCellModel *myCellModel = (JXCategoryTitleImageCellModel *)cellModel;
|
||||
myCellModel.loadImageBlock = self.loadImageBlock;
|
||||
myCellModel.loadImageCallback = self.loadImageCallback;
|
||||
myCellModel.imageType = [self.imageTypes[index] integerValue];
|
||||
myCellModel.imageSize = self.imageSize;
|
||||
myCellModel.titleImageSpacing = self.titleImageSpacing;
|
||||
if (self.imageInfoArray && self.imageInfoArray.count != 0) {
|
||||
myCellModel.imageInfo = self.imageInfoArray[index];
|
||||
}else if (self.imageNames && self.imageNames.count != 0) {
|
||||
myCellModel.imageName = self.imageNames[index];
|
||||
}else if (self.imageURLs && self.imageURLs.count != 0) {
|
||||
myCellModel.imageURL = self.imageURLs[index];
|
||||
}
|
||||
if (self.selectedImageInfoArray && self.selectedImageInfoArray.count != 0) {
|
||||
myCellModel.selectedImageInfo = self.selectedImageInfoArray[index];
|
||||
}else if (self.selectedImageNames && self.selectedImageNames.count != 0) {
|
||||
myCellModel.selectedImageName = self.selectedImageNames[index];
|
||||
}else if (self.selectedImageURLs && self.selectedImageURLs.count != 0) {
|
||||
myCellModel.selectedImageURL = self.selectedImageURLs[index];
|
||||
}
|
||||
myCellModel.imageZoomEnabled = self.imageZoomEnabled;
|
||||
myCellModel.imageZoomScale = ((index == self.selectedIndex) ? self.imageZoomScale : 1.0);
|
||||
}
|
||||
|
||||
- (void)refreshSelectedCellModel:(JXCategoryBaseCellModel *)selectedCellModel unselectedCellModel:(JXCategoryBaseCellModel *)unselectedCellModel {
|
||||
[super refreshSelectedCellModel:selectedCellModel unselectedCellModel:unselectedCellModel];
|
||||
|
||||
JXCategoryTitleImageCellModel *myUnselectedCellModel = (JXCategoryTitleImageCellModel *)unselectedCellModel;
|
||||
myUnselectedCellModel.imageZoomScale = 1.0;
|
||||
|
||||
JXCategoryTitleImageCellModel *myselectedCellModel = (JXCategoryTitleImageCellModel *)selectedCellModel;
|
||||
myselectedCellModel.imageZoomScale = self.imageZoomScale;
|
||||
}
|
||||
|
||||
- (void)refreshLeftCellModel:(JXCategoryBaseCellModel *)leftCellModel rightCellModel:(JXCategoryBaseCellModel *)rightCellModel ratio:(CGFloat)ratio {
|
||||
[super refreshLeftCellModel:leftCellModel rightCellModel:rightCellModel ratio:ratio];
|
||||
|
||||
JXCategoryTitleImageCellModel *leftModel = (JXCategoryTitleImageCellModel *)leftCellModel;
|
||||
JXCategoryTitleImageCellModel *rightModel = (JXCategoryTitleImageCellModel *)rightCellModel;
|
||||
|
||||
if (self.isImageZoomEnabled) {
|
||||
leftModel.imageZoomScale = [JXCategoryFactory interpolationFrom:self.imageZoomScale to:1.0 percent:ratio];
|
||||
rightModel.imageZoomScale = [JXCategoryFactory interpolationFrom:1.0 to:self.imageZoomScale percent:ratio];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)preferredCellWidthAtIndex:(NSInteger)index {
|
||||
if (self.cellWidth == JXCategoryViewAutomaticDimension) {
|
||||
CGFloat titleWidth = [super preferredCellWidthAtIndex:index];
|
||||
JXCategoryTitleImageType type = [self.imageTypes[index] integerValue];
|
||||
CGFloat cellWidth = 0;
|
||||
switch (type) {
|
||||
case JXCategoryTitleImageType_OnlyTitle:
|
||||
cellWidth = titleWidth;
|
||||
break;
|
||||
case JXCategoryTitleImageType_OnlyImage:
|
||||
cellWidth = self.imageSize.width;
|
||||
break;
|
||||
case JXCategoryTitleImageType_LeftImage:
|
||||
case JXCategoryTitleImageType_RightImage:
|
||||
cellWidth = titleWidth + self.titleImageSpacing + self.imageSize.width;
|
||||
break;
|
||||
case JXCategoryTitleImageType_TopImage:
|
||||
case JXCategoryTitleImageType_BottomImage:
|
||||
cellWidth = MAX(titleWidth, self.imageSize.width);
|
||||
break;
|
||||
}
|
||||
return cellWidth;
|
||||
}
|
||||
return self.cellWidth;
|
||||
}
|
||||
|
||||
@end
|
||||
17
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomCell.h
generated
Normal file
17
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomCell.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// JXCategoryTitleVerticalZoomCell.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2019/2/14.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCell.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface JXCategoryTitleVerticalZoomCell : JXCategoryTitleCell
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
45
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomCell.m
generated
Normal file
45
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomCell.m
generated
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// JXCategoryTitleVerticalZoomCell.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2019/2/14.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleVerticalZoomCell.h"
|
||||
#import "JXCategoryTitleVerticalZoomCellModel.h"
|
||||
|
||||
@implementation JXCategoryTitleVerticalZoomCell
|
||||
|
||||
- (void)reloadData:(JXCategoryBaseCellModel *)cellModel {
|
||||
[super reloadData:cellModel];
|
||||
|
||||
JXCategoryTitleVerticalZoomCellModel *myCellModel = (JXCategoryTitleVerticalZoomCellModel *)cellModel;
|
||||
|
||||
if (myCellModel.isTitleLabelZoomEnabled) {
|
||||
//先把font设置为缩放的最大值,再缩小到最小值,最后根据当前的titleLabelZoomScale值,进行缩放更新。这样就能避免transform从小到大时字体模糊
|
||||
UIFont *maxScaleFont = [UIFont fontWithDescriptor:myCellModel.titleFont.fontDescriptor size:myCellModel.titleFont.pointSize*myCellModel.maxVerticalFontScale];
|
||||
CGFloat baseScale = myCellModel.titleFont.lineHeight/maxScaleFont.lineHeight;
|
||||
if (myCellModel.isSelectedAnimationEnabled && [self checkCanStartSelectedAnimation:myCellModel]) {
|
||||
JXCategoryCellSelectedAnimationBlock block = [self preferredTitleZoomAnimationBlock:myCellModel baseScale:baseScale];
|
||||
[self addSelectedAnimationBlock:block];
|
||||
} else {
|
||||
self.titleLabel.font = maxScaleFont;
|
||||
self.maskTitleLabel.font = maxScaleFont;
|
||||
CGAffineTransform currentTransform = CGAffineTransformMakeScale(baseScale*myCellModel.titleLabelCurrentZoomScale, baseScale*myCellModel.titleLabelCurrentZoomScale);
|
||||
self.titleLabel.transform = currentTransform;
|
||||
self.maskTitleLabel.transform = currentTransform;
|
||||
}
|
||||
} else {
|
||||
if (myCellModel.isSelected) {
|
||||
self.titleLabel.font = myCellModel.titleSelectedFont;
|
||||
self.maskTitleLabel.font = myCellModel.titleSelectedFont;
|
||||
}else {
|
||||
self.titleLabel.font = myCellModel.titleFont;
|
||||
self.maskTitleLabel.font = myCellModel.titleFont;
|
||||
}
|
||||
}
|
||||
|
||||
[self.titleLabel sizeToFit];
|
||||
}
|
||||
@end
|
||||
19
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomCellModel.h
generated
Normal file
19
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomCellModel.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// JXCategoryTitleVerticalZoomCellModel.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2019/2/14.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleCellModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface JXCategoryTitleVerticalZoomCellModel : JXCategoryTitleCellModel
|
||||
|
||||
@property (nonatomic, assign) CGFloat maxVerticalFontScale;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
13
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomCellModel.m
generated
Normal file
13
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomCellModel.m
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// JXCategoryTitleVerticalZoomCellModel.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2019/2/14.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleVerticalZoomCellModel.h"
|
||||
|
||||
@implementation JXCategoryTitleVerticalZoomCellModel
|
||||
|
||||
@end
|
||||
34
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomView.h
generated
Normal file
34
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomView.h
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// JXCategoryTitleVerticalZoomView.h
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2019/2/14.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
垂直方向的缩放值范围:minVerticalFontScale~maxVerticalFontScale;
|
||||
垂直方向cellSpacing范围:minVerticalCellSpacing~maxVerticalCellSpacing,用于达到缩小时cell更加紧凑
|
||||
根据UI设计师给你的参数,去多次尝试设置上面的值来达到同样的效果。多尝试几次就知道每个属性设置之后的效果。
|
||||
*/
|
||||
@interface JXCategoryTitleVerticalZoomView : JXCategoryTitleView
|
||||
|
||||
@property (nonatomic, assign) CGFloat maxVerticalFontScale; //垂直方向最大的缩放值
|
||||
@property (nonatomic, assign) CGFloat minVerticalFontScale; //垂直方向最小的缩放值
|
||||
@property (nonatomic, assign) CGFloat maxVerticalCellSpacing; //垂直方向最大的cellSpacing
|
||||
@property (nonatomic, assign) CGFloat minVerticalCellSpacing; //垂直方向最小的cellSpacing
|
||||
|
||||
/**
|
||||
当前列表滚动时,根据当前垂直方向categoryView高度变化的百分比,刷新布局
|
||||
|
||||
@param percent 当前垂直方向categoryView高度变化百分比
|
||||
*/
|
||||
- (void)listDidScrollWithVerticalHeightPercent:(CGFloat)percent;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
94
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomView.m
generated
Normal file
94
Pods/JXCategoryView/Sources/VerticalZoomTitle/JXCategoryTitleVerticalZoomView.m
generated
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// JXCategoryTitleVerticalZoomView.m
|
||||
// JXCategoryView
|
||||
//
|
||||
// Created by jiaxin on 2019/2/14.
|
||||
// Copyright © 2019 jiaxin. All rights reserved.
|
||||
//
|
||||
|
||||
#import "JXCategoryTitleVerticalZoomView.h"
|
||||
#import "JXCategoryTitleVerticalZoomCellModel.h"
|
||||
#import "JXCategoryTitleVerticalZoomCell.h"
|
||||
#import "JXCategoryFactory.h"
|
||||
|
||||
@interface JXCategoryTitleVerticalZoomView ()
|
||||
@property (nonatomic, assign) CGFloat currentVerticalScale; //当前垂直方向的缩放基准值
|
||||
@end
|
||||
|
||||
@implementation JXCategoryTitleVerticalZoomView
|
||||
|
||||
- (void)initializeData {
|
||||
[super initializeData];
|
||||
|
||||
_maxVerticalFontScale = 2;
|
||||
_minVerticalFontScale = 1.3;
|
||||
_currentVerticalScale = _maxVerticalFontScale;
|
||||
self.cellWidthZoomEnabled = YES;
|
||||
self.cellWidthZoomScale = _maxVerticalFontScale;
|
||||
self.contentEdgeInsetLeft = 15;
|
||||
self.titleLabelZoomScale = _currentVerticalScale;
|
||||
self.titleLabelZoomEnabled = YES;
|
||||
self.selectedAnimationEnabled = YES;
|
||||
_maxVerticalCellSpacing = 20;
|
||||
_minVerticalCellSpacing = 10;
|
||||
self.cellSpacing = _maxVerticalCellSpacing;
|
||||
}
|
||||
|
||||
- (void)listDidScrollWithVerticalHeightPercent:(CGFloat)percent {
|
||||
CGFloat currentScale = [JXCategoryFactory interpolationFrom:self.minVerticalFontScale to:self.maxVerticalFontScale percent:percent];
|
||||
BOOL shouldReloadData = NO;
|
||||
if (self.currentVerticalScale != currentScale) {
|
||||
//有变化才允许reloadData
|
||||
shouldReloadData = YES;
|
||||
}
|
||||
self.currentVerticalScale = currentScale;
|
||||
self.cellWidthZoomScale = currentScale;
|
||||
self.cellSpacing = [JXCategoryFactory interpolationFrom:self.minVerticalCellSpacing to:self.maxVerticalCellSpacing percent:percent];
|
||||
if (shouldReloadData) {
|
||||
[self refreshDataSource];
|
||||
[self refreshState];
|
||||
[self.collectionView.collectionViewLayout invalidateLayout];
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCurrentVerticalScale:(CGFloat)currentVerticalScale {
|
||||
_currentVerticalScale = currentVerticalScale;
|
||||
|
||||
self.titleLabelZoomScale = currentVerticalScale;
|
||||
}
|
||||
|
||||
- (void)setMaxVerticalCellSpacing:(CGFloat)maxVerticalCellSpacing {
|
||||
_maxVerticalCellSpacing = maxVerticalCellSpacing;
|
||||
|
||||
self.cellSpacing = maxVerticalCellSpacing;
|
||||
}
|
||||
|
||||
- (void)setMaxVerticalFontScale:(CGFloat)maxVerticalFontScale {
|
||||
_maxVerticalFontScale = maxVerticalFontScale;
|
||||
|
||||
self.titleLabelZoomScale = maxVerticalFontScale;
|
||||
self.cellWidthZoomScale = maxVerticalFontScale;
|
||||
}
|
||||
|
||||
- (Class)preferredCellClass {
|
||||
return [JXCategoryTitleVerticalZoomCell class];
|
||||
}
|
||||
|
||||
- (void)refreshDataSource {
|
||||
NSMutableArray *tempArray = [NSMutableArray array];
|
||||
for (int i = 0; i < self.titles.count; i++) {
|
||||
JXCategoryTitleVerticalZoomCellModel *cellModel = [[JXCategoryTitleVerticalZoomCellModel alloc] init];
|
||||
[tempArray addObject:cellModel];
|
||||
}
|
||||
self.dataSource = tempArray;
|
||||
}
|
||||
|
||||
- (void)refreshCellModel:(JXCategoryBaseCellModel *)cellModel index:(NSInteger)index {
|
||||
[super refreshCellModel:cellModel index:index];
|
||||
|
||||
JXCategoryTitleVerticalZoomCellModel *model = (JXCategoryTitleVerticalZoomCellModel *)cellModel;
|
||||
model.maxVerticalFontScale = self.maxVerticalFontScale;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user