iOS实战-自定义的横向滚动控件CustomScrollView
来源:互联网 发布:浙江大学网络运行系统 编辑:程序博客网 时间:2024/05/09 05:50
CustomScrollView
使用官方UIScrollView组件定制的一个横向滚动的视图。由于能力有限,暂没有抽象成一个UI组件,如果有大神能进行抽象封装,非常欢迎,大家多多交流!
1.1 说明
CustomScrollView包括诺干个子视图,可以横向滚动,滚动过程中会根据子视图所在位置进行大小缩放。即最中间的视图最大,两边呈对称状态逐渐减小。且可以通过点击按钮进行滚动,选定某个子视图居中。还可以动态进行新增和删除子视图的操作,其中删除操作为在子视图上进行上滑手势操作。
Github 项目传送门——CustomScrollView
1.2 截图
二、具体实现
接下来我们来看看是怎么一步一步实现这种效果的。
2.1 模型
这里的模型只是我们简单定义的一个数据模型,模型包含了一个名称和对应的logo图标的名字。
//YSModel.h
#import <Foundation/Foundation.h>@interface YSModel : NSObject@property (copy, nonatomic) NSString *name;@property (copy, nonatomic) NSString *logoName;- (instancetype)initWithName:(NSString *)name logoName:(NSString *)logoName;@end
//YSModel.m
#import "YSModel.h"@implementation YSModel- (instancetype)init{ return [self initWithName:@"自定义" logoName:@"custom"];}- (instancetype)initWithName:(NSString *)name logoName:(NSString *)logoName{ self = [super init]; if (self) { _name = name; _logoName = logoName; } return self;}@end
2.2 界面实现和控件绑定
界面直接在xib文件里实现。只需要一个UIScrollView和UILabel就可以了,UILabel是为了当UIScrollView中的子视图滚动式,也会跟着切换。效果如图:
2.3 ViewController的实现
首先我们需要一些宏定义的常量:
//默认scrollView显示的模型数目#define MODEL_NUMBER 5//屏幕宽度#define UISCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width//默认图标缩放比率#define SCALE_RATE 0.6
其次我们需要一些变量,比如Outlet变量,数据模型数组等变量,详见demon里的代码。接下来我们主要详细介绍几个重点方法。
2.3.1 计算每个cell的宽高,以及初始化数据和UI视图
- (void)viewDidLoad{ [super viewDidLoad]; //计算ScrollView中每个cell的宽高 self.cellWidth = self.scrollView.frame.size.width / MODEL_NUMBER; self.cellHeight = self.scrollView.frame.size.height; [self initModels]; [self initScrollView];}
1.[self initModels] 方法主要是初始化模型数据,比较简单。
2.其主要的ScrollView初始化实现为方法[self initScrollView]。
该方法中,主要设置了ScrollView的初始化属性,例如禁用水平,垂直方向的滚动条,设定ScrollView初始位置等。
- (void)initScrollView{ //设定scrollView的contentSize,即scrollView中包含的cell个数计算出来的内容大小 //+4是因为前后分别有两个空白的cell视图 self.scrollView.contentSize = CGSizeMake(self.cellWidth * (self.models.count + 4), self.cellHeight); //清除scrollView的子视图 //[self.scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; //设置scrollView的委托对象 self.scrollView.delegate = self; //隐藏水平和竖直方向的滚动条 self.scrollView.showsHorizontalScrollIndicator = NO; self.scrollView.showsVerticalScrollIndicator = YES; //设置scrollView滚动的减速速率 self.scrollView.decelerationRate = 0.95f; if (!self.cellView) { self.cellView = [NSMutableArray array]; } else { [self.cellView removeAllObjects]; } //添加两个空白的cell块 for (int i = 0; i < 2; i++) { UIView *view = [self createEmptyCell:CGRectMake(self.cellWidth * i, 0, self.cellWidth, self.cellHeight)]; [self.scrollView addSubview:view]; } //默认的六个块 for (int i = 2; i < self.models.count + 2; i++) { UIView *view = [[UIView alloc] initWithFrame:CGRectMake(self.cellWidth * i, 0, self.cellWidth, self.cellHeight)]; //创建一个ImageView用于显示图标logo UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, self.cellWidth - 10, self.cellWidth - 10)]; //设置图片为logo图片 image.image = [UIImage imageNamed:[self.models[i - 2] logoName]]; //开启可交互模式 [image setUserInteractionEnabled:YES]; image.tag = i - 2; view.tag = i -2; //最后一个"自定义"按钮添加特定触摸手势 if (i == self.models.count + 1) { UITapGestureRecognizer *tapAddModel = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToAddModel:)]; [image addGestureRecognizer:tapAddModel]; } //别的模型添加点击手势和向上滑动删除手势 else { //添加点击手势 UITapGestureRecognizer *tapEditModel = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToEditModel:)]; [image addGestureRecognizer:tapEditModel]; //添加滑动手势 UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeToDeleteModel:)]; //设置滑动方向为向上 [swipeGesture setDirection:UISwipeGestureRecognizerDirectionUp]; [image addGestureRecognizer:swipeGesture]; } [view addSubview:image]; //记录下对应的cell视图 [self.cellView addObject:view]; [self.scrollView addSubview:view]; } //添加两个空白的块 for (long i = self.models.count + 2; i < self.models.count + 4; i++) { UIView *view = [self createEmptyCell:CGRectMake(self.cellWidth * i, 0, self.cellWidth, self.cellHeight)]; [self.scrollView addSubview:view]; } //设置默认居中为第三个模型 [self.scrollView setContentOffset:CGPointMake(self.cellWidth * 2, 0) animated:YES]; self.cellIndex = 2; //设置背景颜色和文字 [self updateCellBackground:(int)self.cellIndex];}//创建空白cell视图- (UIView *)createEmptyCell:(CGRect)frame{ UIView *view = [[UIView alloc] initWithFrame:frame]; //设置背景透明 view.backgroundColor = [UIColor clearColor]; return view;}
此时我们应该会得到这样一个界面了。
2.3.2 实现UIScrollViewDelegate
我们的很多滚动动画效果都是基于UIScrollViewDelegate中的回调方法的。接下来我们就看看如何实现这些效果。
首先我们看看官方API中UIScrollView有哪些协议方法。
我们这里用的主要是这几个方法。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
接下来我们详细看一下每个方法的实现。
(void)scrollViewDidScroll:(UIScrollView *)scrollView
//滑动过程中回调的函数,无论是手动滑动的,还是代码动画滑动都会回调该方法//在这里计算那个cell是可见的,然后计算缩放比例,进行动画缩放-(void)scrollViewDidScroll:(UIScrollView *)scrollView{ //处理每一个cell,计算它的缩放比例 for (int i = 0; i < self.models.count; i++) { //cell左侧x位置 float lead = self.cellWidth * (i + 2); //cell右侧x位置 float tail = self.cellWidth * (i + 3); float rate = SCALE_RATE; //cell在屏幕左,右侧,不可见,设置为默认缩放比例0.6 if (self.scrollView.contentOffset.x >= tail || (self.scrollView.contentOffset.x + UISCREEN_WIDTH) <= lead) { //暂时啥都不干 } //cell在屏幕上 else { float sub = lead - self.scrollView.contentOffset.x; //前半部分 if (sub <= 2 * self.cellWidth) { rate = sub / (2 * self.cellWidth) * SCALE_RATE + SCALE_RATE; } else { rate = (UISCREEN_WIDTH - sub - self.cellWidth) / (2 * self.cellWidth) * SCALE_RATE + SCALE_RATE; } } //缩放该cell的视图 [self viewToScale:rate target:self.cellView[i]]; }}
(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
//scrollView 拖拽操作结束 //判断接下来是否会进行减速操作,如果不需要减速则在这里进行计算,得出当前那个cell最靠近中间位置,并把该cell滑动到居中的位置//否则,不做任何处理。其实则就是要进行减速,减速完毕会回调scrollViewDidEndDecelerating。//综上,都会计算需要居中哪个cell- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ if (!decelerate) { [self cellJumpToIndex:scrollView]; }}
(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
//scrollView 滑动过程减速完毕后回调的方法//在这里进行计算,得出当前那个cell最靠近中间位置,并把该cell滑动到居中的位置- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ [self cellJumpToIndex:scrollView];}
(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
//滑动动画结束时调用的函数- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{ //根据居中的选项更新背景和文字 [self updateCellBackground:(int)self.cellIndex]; [self.scrollView setUserInteractionEnabled:YES];}
我们要实现自动滑动居中的效果,这里有一个很关键的方法。该方法用于计算当前最靠近居中位置的是哪一个cell子视图。我们首先计算当前ScrollView的contentOffset.x的位置,从而得知当前显示的cell有哪些,然后计算出处于中间位置的cell的索引下标,再计算出该cell居中时的contentOffset.x位置,再进行动画移动到该位置即可。
- (void)cellJumpToIndex:(UIScrollView *)scrollView{ if (self.scrollView.contentOffset.x < self.cellWidth * 0.5) { [self.scrollView setContentOffset:CGPointMake(0, 0) animated:YES]; } else if (self.scrollView.contentOffset.x > self.cellWidth * (self.models.count + 1.5)) { [self.scrollView setContentOffset:CGPointMake(self.cellWidth * (self.models.count + 1), 0) animated:YES]; } int index = (int)(self.scrollView.contentOffset.x / self.cellWidth + 0.5); [self.scrollView setContentOffset:CGPointMake(self.cellWidth * index, 0) animated:YES]; //选定某个模式,进行模式更新等操作 self.cellIndex = index;}
最后就是一个是进行缩放的方法,还有一个更新cell对应的视图的方法。
//按比例缩放视图- (void)viewToScale:(float)scale target:(UIView *)view{ UIImageView *image = [[view subviews] lastObject]; [UIView beginAnimations:@"scale" context:nil]; image.transform = CGAffineTransformMakeScale(scale, scale); [UIView commitAnimations];}//滑动到某个cell时更新视图的方法- (void)updateCellBackground:(int)index{ self.name.text = [self.models[index] name];}
此时我们基本可以实现一开始希望得到的滚动缩放效果了。接下来我们的任务就是实现动态的cell增加和删除等功能。
期待ing…
三、个人博客
林友松。一个逗比的开发者。
Email:lysongzi.hnu@gmail.com
博客地址:lysongzi.com
- iOS实战-自定义的横向滚动控件CustomScrollView
- 自定义滚的CustomScrollView
- [IOS]UITableView横向滚动
- *控件*有横向和竖向的滚动条的DataGrid(横向支持从第几列滚动)
- 自定义控件HorizontalListView,横向的ListView
- 自定义控件HorizontalListView,横向的ListView
- android自定义控件--横向滑动的ListView
- 自定义文本横向渐变消失的控件
- ios 横向日历控件的使用
- Android 自定义横向滚动条
- 给你的list控件添加横向的滚动条
- ScrollerLayout——可横向滚动的自定义viewgroup
- 自定义Viewgroup(2)--可滚动的横向布局
- iOS之tableView横向滚动
- 横向滚动的ListView
- 横向滚动的tableview
- iOS UIScrollview 横向滚动 以及竖向滚动
- (C#版)*控件*有横向和竖向的滚动条的DataGrid(横向支持从第几列滚动)
- android中“后台”更新Activity的办法(application handler和BroadcastReceiver)
- 内存管理
- java中XML格式的字符串4读取方式的简单比较
- Android自动化测试之Monkeyrunner使用方法及实例
- 数据结构-6
- iOS实战-自定义的横向滚动控件CustomScrollView
- Uva1368
- Python学习笔记 - 3.函数
- 基于Spring的app后台开源框架
- final的使用
- 在 Linux 系统中使用 rm -rf /* 命令
- Java中建造者(Builder)设计
- swift与OC的关系
- 手机web端资源整合