ios之页面切换------自定义功能按钮事件
OC版写一个快速集成网易新闻,腾讯视频,头条首页的ZJScro...

项目介绍:
<ignore_js_op>
一.构思部分:
打算分为三个部分, 滑块部分View, 内容显示部分View, 包含滑块View和显示内容View的View,以便于可以灵活的使用
1. 滑块部分View
1.1 要实现滑块可以滚动, 考虑可以直接使用collectionView, 但是这里还是直接使用scrollView方便里面的控件布局
1.2 要实现滑块的点击, 可以直接使用UIButton, 但是经过尝试, 要让button的frame随着文字的宽度来自适应实现比较麻烦, 所以选择了使用UILabel添加点击手势来实现点击事件,这里使用了closures来实现(可以使用代理模式)
1.3 实现对应的滚动条和遮盖同步移动的效果,文字颜色渐变功能(在点击的时候直接使用一个动画就可以简单的完成了)
2. 内容显示部分View
2.1 用来作为包含子控制器的view的容器, 并且实现可以分页滚动的效果
2.2 要实现分页滚动, 可以使用UIScrollView来实现, 但是这样就要考虑UIScrollView上的各个view的复用的问题, 其中细节还是很麻烦, 所以直接使用了UICollectionView(利用他的重用机制)来实现
2.3 将每一个子控制器的view直接添加到对应的每一个cell的contentView中来展示, 所以这里需要注意cell重用可能带来的内容显示不正常的问题, 这里采用了每次添加contentView的内容时移除所有的subviews(也可以直接给每个cell用不同的reuseIdentifier实现)
2.4 实现实时监控滚动的进度提供给滑块部分来同步调整滚动条和遮盖,文字颜色的渐变, 并且在每次滚动完成的时候可以通知给滑块来调整他的内容
3. 包含滑块View和显示内容View的View
3.1 因为滑块部分View和内容显示部分View是相对独立的部分, 在这里只需要实现两者的通信即可
3.2 可以自定义滑块部分View和内容显示部分View的frame
a. 滑块部分1. 基本属性
逻辑处理
b 内容显示部分View
基本属性
逻辑处理
3. "ZJScrollPageView.h"
Swift 版本 :http://www.code4app.com/thread-7596-1-1.html
DEMO 下载:
一.构思部分:
打算分为三个部分, 滑块部分View, 内容显示部分View, 包含滑块View和显示内容View的View,以便于可以灵活的使用
1. 滑块部分View
1.1 要实现滑块可以滚动, 考虑可以直接使用collectionView, 但是这里还是直接使用scrollView方便里面的控件布局
1.2 要实现滑块的点击, 可以直接使用UIButton, 但是经过尝试, 要让button的frame随着文字的宽度来自适应实现比较麻烦, 所以选择了使用UILabel添加点击手势来实现点击事件,这里使用了closures来实现(可以使用代理模式)
1.3 实现对应的滚动条和遮盖同步移动的效果,文字颜色渐变功能(在点击的时候直接使用一个动画就可以简单的完成了)
2. 内容显示部分View
2.1 用来作为包含子控制器的view的容器, 并且实现可以分页滚动的效果
2.2 要实现分页滚动, 可以使用UIScrollView来实现, 但是这样就要考虑UIScrollView上的各个view的复用的问题, 其中细节还是很麻烦, 所以直接使用了UICollectionView(利用他的重用机制)来实现
2.3 将每一个子控制器的view直接添加到对应的每一个cell的contentView中来展示, 所以这里需要注意cell重用可能带来的内容显示不正常的问题, 这里采用了每次添加contentView的内容时移除所有的subviews(也可以直接给每个cell用不同的reuseIdentifier实现)
2.4 实现实时监控滚动的进度提供给滑块部分来同步调整滚动条和遮盖,文字颜色的渐变, 并且在每次滚动完成的时候可以通知给滑块来调整他的内容
3. 包含滑块View和显示内容View的View
3.1 因为滑块部分View和内容显示部分View是相对独立的部分, 在这里只需要实现两者的通信即可
3.2 可以自定义滑块部分View和内容显示部分View的frame
a. 滑块部分1. 基本属性
[Objective-C] 查看源文件 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// 滚动条@property (weak, nonatomic) UIView *scrollLine;// 遮盖@property (weak, nonatomic) UIView *coverLayer;// 滚动scrollView@property (weak, nonatomic) UIScrollView *scrollView;// 背景ImageView@property (weak, nonatomic) UIImageView *backgroundImageView;// 附加的按钮@property (weak, nonatomic) UIButton *extraBtn;/// 所有的title设置 -> 使用了一个class ZJSegmentStyle, 将可以自定义的部分全部暴露了出来, 使用的时候就可以比较方便的自定义很多属性 -> 初始化时传入@property (strong, nonatomic) ZJSegmentStyle *segmentStyle;// 所有的标题@property (strong, nonatomic) NSArray *titles;// 用于懒加载计算文字的rgb差值, 用于颜色渐变的时候设置@property (strong, nonatomic) NSArray *deltaRGB;@property (strong, nonatomic) NSArray *selectedColorRgb;@property (strong, nonatomic) NSArray *normalColorRgb;/** 缓存所有标题label */@property (nonatomic, strong) NSMutableArray *titleLabels;// 缓存计算出来的每个标题的宽度@property (nonatomic, strong) NSMutableArray *titleWidths;// 响应标题点击@property (copy, nonatomic) TitleBtnOnClickBlock titleBtnOnClick; |
逻辑处理
[Objective-C] 查看源文件 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
#pragma mark - life cycle- (instancetype)initWithFrame:(CGRect )frame segmentStyle:(ZJSegmentStyle *)segmentStyle titles:(NSArray *)titles titleDidClick:(TitleBtnOnClickBlock)titleDidClick { if (self = [super initWithFrame:frame]) { self.segmentStyle = segmentStyle; self.titles = titles; self.titleBtnOnClick = titleDidClick; _currentIndex = 0; _oldIndex = 0; _currentWidth = frame.size.width; if (!self.segmentStyle.isScrollTitle) { // 不能滚动的时候就不要把缩放和遮盖或者滚动条同时使用, 否则显示效果不好 self.segmentStyle.scaleTitle = !(self.segmentStyle.isShowCover || self.segmentStyle.isShowLine); } // 这个函数里面设置了基本属性中的titles, labelsArray, titlesWidthArray,并且添加了label到scrollView上 [self setupTitles]; // 这个函数里面设置了遮盖, 滚动条,和label的初始化位置 [self setupUI]; } return self;}#pragma mark - button action -> 处理点击title的时候实现标题的切换,和遮盖,滚动条...的位置调整, 同时执行了相应点击得儿blosure, 以便于外部相应点击方法- (void)titleLabelOnClick:(UITapGestureRecognizer *)tapGes { ZJCustomLabel *currentLabel = (ZJCustomLabel *)tapGes.view; if (!currentLabel) { return; } _currentIndex = currentLabel.tag; // 同步更改UI [self adjustUIWhenBtnOnClickWithAnimate:true];}- (void)adjustTitleOffSetToCurrentIndex:(NSInteger)currentIndex -> 更改scrollview的contentOffSet来居中显示title // 手动滚动时需要提供动画效果- (void)adjustUIWithProgress:(CGFloat)progress oldIndex:(NSInteger)oldIndex currentIndex:(NSInteger)currentIndex -> 提供给外部来执行标题切换之间的动画效果(注意这个方法里面进行了一些简单的数学计算以便于"同步" 滚动滚动条和cell ) 这里以滑块的位置x变化为例, 其他类似 CGFloat xDistance = currentLabel.zj_x - oldLabel.zj_x; 这个xDistance就是滑块将要从一个label下面移动到下一个label下面所需要移动的路程, 这个progress是外界提供来的, 表示当前已经移动的百分比(0 --- 1)是多少了,所以可以改变当前滑块的x为之前的x + 已经完成滚动的距离(xDistance * progress) scrollLine?.frame.origin.x = oldLabel.frame.origin.x + xDistance * progress 这样就达到了滑块的位置随着提供的progress同步移动 其他的遮盖 和颜色渐变的处理类似 |
b 内容显示部分View
基本属性
[Objective-C] 查看源文件 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
// 用于处理重用和内容的显示@property (weak, nonatomic) UICollectionView *collectionView;// collectionView的布局@property (strong, nonatomic) UICollectionViewFlowLayout *collectionViewLayout;/** 避免循环引用*/@property (weak, nonatomic) ZJScrollSegmentView *segmentView;@property (weak, nonatomic) UIButton *extraBtn;// 父类 用于处理添加子控制器 使用weak避免循环引用@property (weak, nonatomic) UIViewController *parentViewController;// 当这个属性设置为YES的时候 就不用处理 scrollView滚动的计算@property (assign, nonatomic) BOOL forbidTouchToAdjustPosition;// 所有的子控制器@property (strong, nonatomic) NSArray *childVcs; |
逻辑处理
[Objective-C] 查看源文件 复制代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
#pragma mark - life cycle - (instancetype)initWithFrame:(CGRect)frame childVcs:(NSArray *)childVcs segmentView:(ZJScrollSegmentView *)segmentView parentViewController:(UIViewController *)parentViewController { if (self = [super initWithFrame:frame]) { self.childVcs = childVcs; self.parentViewController = parentViewController; self.segmentView = segmentView; _oldIndex = 0; _currentIndex = 1; _oldOffSetX = 0.0; self.forbidTouchToAdjustPosition = NO; // 触发懒加载 self.collectionView.backgroundColor = [UIColor whiteColor]; [self commonInit]; } return self;}/** 给外界可以设置ContentOffSet的方法 比如点击了标题的时候需要更改内容显示调用*/- (void)setContentOffSet:(CGPoint)offset animated:(BOOL)animated/** 给外界刷新视图的方法 用于动态设置子控制器和标题*/- (void)reloadAllViewsWithNewChildVcs:(NSArray *)newChileVcs { // 这种处理是结束子控制器和父控制器的关系 for (UIViewController *childVc in self.childVcs) { [childVc willMoveToParentViewController:nil]; [childVc.view removeFromSuperview]; [childVc removeFromParentViewController]; }}#pragma mark - UICollectionViewDelegate --- UICollectionViewDataSource- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1;}- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.childVcs.count;}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath]; // 移除subviews 避免重用内容显示错误 [cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; // 这里建立子控制器和父控制器的关系 -> 当然在这之前已经将对应的子控制器添加到了父控制器了, 只不过还没有建立完成 UIViewController *vc = (UIViewController *)self.childVcs[indexPath.row]; vc.view.frame = self.bounds; [cell.contentView addSubview:vc.view]; [vc didMoveToParentViewController:self.parentViewController]; return cell;}- (void)scrollViewDidScroll:(UIScrollView *)scrollView -> 这个代理方法中处理了滚动的下标和进度 |
3. "ZJScrollPageView.h"
这一部分是将segmentView和ContentView封装在一起的, 便于使用者直接使用, 但是您也可以参照这里面的实现, 任意组合segmentView和ContentView的位置, 在demo中也有示例
Swift 版本 :http://www.code4app.com/thread-7596-1-1.html
DEMO 下载:

浙公网安备 33010602011771号