本文主要介绍如何实现当UIScrollview滑动时,自动隐藏和显示标题栏的效果。其中布局采用Autolayout的方式,并且为了代码精简,使用了第三方库Masonry,其使用参考【iOS-Masonry学习笔记】。使用它的原因是可以很好的结合动画效果的实现!
首先我们来介绍一下整个布局。最外层的是一个UIScrollview(mainScrollview),其子视图包括一个模拟标题栏的UIView视图,以及一个UIScrollview视图(innerScrollview),并且其是按顺序上下排列的。其中的innerScrollview包含一个UIView容器子视图,并且该容器中包括若干个UIView子视图来模拟cell。其显示效果大致如下:
//屏幕宽度
#define UIScreenWidth [[UIScreen mainScreen] bounds].size.width
//屏幕高度
#define UIScreenHeight [[UIScreen mainScreen] bounds].size.height
//标题栏高度
NSInteger const titleHeight = 100;
//每个cell高度
NSInteger const cellHeight = 80;
//触发标题栏隐藏和显示事件的scrollview在Y方向上的滑动位移阈值
NSInteger const triggerToHideY = 200;
整个布局的视图属性并不多,主要是一下这些。其中cells数组用于存储添加到container中的子UIView视图,以便于此后的视图约束设置。还有一个属性isHide是用来表示标题的状态的,如果标题隐藏则为YES,反之为NO,默认值为NO;
@property (strong, nonatomic) UIScrollView *mainScrollview;
@property (strong, nonatomic) UIView *hideView;
@property (strong, nonatomic) UIScrollView *innerScrollview;
@property (strong, nonatomic) UIView *container;
@property (strong, nonatomic) NSMutableArray *cells;
@property (nonatomic) BOOL isHide;
接下来则是手动初始化各个视图对象,并设置它们的父子关系。
- (void)viewDidLoad {
[super viewDidLoad];
//设置默认值
self.isHide = NO;
//初始化cells数组
self.cells = [NSMutableArray new];
//初始化mainScrollview视图
self.mainScrollview = [UIScrollView new];
[self.mainScrollview setBackgroundColor:[UIColor whiteColor]];
[self.view addSubview:self.mainScrollview];
//初始化hideView视图
self.hideView = [UIView new];
[self.hideView setBackgroundColor:[UIColor colorWithRed:0.000 green:0.502 blue:1.000 alpha:1.000]];
[self.mainScrollview addSubview:self.hideView];
//初始化container
self.container = [UIView new];
[self.container setBackgroundColor:[UIColor whiteColor]];
//初始化innerScrollView,默认开启弹簧效果
self.innerScrollview = [UIScrollView new];
self.innerScrollview.delegate = self;
//self.innerScrollview.bounces = NO;
[self.innerScrollview setBackgroundColor:[UIColor blackColor]];
[self.innerScrollview addSubview:self.container];
[self.mainScrollview addSubview:self.innerScrollview];
//生成若干个子视图,并添加到container中
for (int i = 0; i < 20; i++) {
UIView *view = [[UIView alloc] init];
[view setBackgroundColor:[UIColor colorWithRed:1-(i*10.0/255) green:1-(i*10.0/255) blue:1-(i*10.0/255) alpha:1.0f]];
[self.container addSubview:view];
[self.cells addObject:view];
}
}
视图的约束主要是采用Autolayout的布局思路,并使用第三方框架Masonry。哈哈大家可以看到使用框架之后省了好多代码量哈哈哈。
-(void)updateViewConstraints
{
//设置mainScrollview约束
[self.mainScrollview mas_makeConstraints:^(MASConstraintMaker *make) {
//其边距与sel.view相等,即全屏显示
make.edges.equalTo(self.view);
}];
//设置hideView约束
[self.hideView mas_makeConstraints:^(MASConstraintMaker *make) {
//其上,左,右边距紧靠mainScrollview
make.top.left.right.equalTo(self.mainScrollview);
//X方向上居中
make.centerX.equalTo(self.mainScrollview);
//设置标题的高度
make.height.equalTo(@(titleHeight));
}];
//设置innerScrollview约束
[self.innerScrollview mas_makeConstraints:^(MASConstraintMaker *make) {
//其top紧靠标题的bottom,即它位于标题下方
make.top.equalTo(self.hideView.mas_bottom);
//左,右,下紧靠mainScrollview
make.left.and.right.equalTo(self.mainScrollview);
make.centerY.equalTo(self.mainScrollview).with.centerOffset(CGPointMake(0, titleHeight));
}];
//设置container约束
[self.container mas_makeConstraints:^(MASConstraintMaker *make) {
//containt主要约束为和innerScrollview的大小一致
make.edges.equalTo(self.innerScrollview);
make.width.equalTo(self.innerScrollview);
}];
//设置每个cell的约束
for (int i = 0; i < self.cells.count; i++) {
//获取需要约束的视图
UIView *subview = self.cells[i];
[subview mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.left.centerX.equalTo(self.container);
make.height.equalTo(@(cellHeight));
//如果是第一个cell,则其top属性紧靠container容器
//否则每个cell的top属性紧靠上一个cell的bottom属性
if (i == 0) {
make.top.equalTo(self.container);
}
else{
UIView *topView = self.cells[i - 1];
make.top.equalTo(topView.mas_bottom);
}
}];
}
//设置容器底部约束
[self.container mas_makeConstraints:^(MASConstraintMaker *make) {
//约束容器的bottom紧靠最后一个cell的bottom
//完成这个约束InnerScrollview就可以自动计算contentSize
//然后就可以滑动了!很神奇是不是!
UIView *lastView = self.cells[self.cells.count - 1];
make.bottom.equalTo(lastView.mas_bottom);
}];
//最后不要忘了调用超类的方法
[super updateViewConstraints];
}
接下来就是如何实现自动隐藏和显示了。其实这个也很简单,了解UIScrollview的就会知道其有一个协议为UIScrollViewDelegate
,其中包括了一些当scrollview滑动时会回调的函数,滑动动画开始、结束时的回调,用户手指拖拽和结束拖拽等诸多事件的回调。在这里我们主要用到的回调方法为scrollViewDidScroll:
,就是当scrollview出现滑动事件时就会回调的方法。
所以首先要实现该协议。
@interface ViewController () <UIScrollViewDelegate>
然后设置innerScrollview的delegate属性。
self.innerScrollview.delegate = self;
最后则是实现scrollViewDidScroll:
方法。在方法里,先判断scrollview滑动的距离是否达到了触发自动隐藏和显示的阈值,然后判断当前标题栏的状态再是否需要进行动画隐藏和显示。其中动画实现的原理很简单,当需要隐藏标题栏时,则将标题视图移出视图(可以考虑将其也隐藏),并且重新设置InnerScrollview的显示区域。(更多UIScrollView相关参考【iOS实战-自定义的横向滚动控件CustomScrollView】)
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.y > triggerToHideY) {
if (!self.isHide) {
self.isHide = YES;
[UIView animateWithDuration:0.3 animations:^{
self.hideView.center = CGPointMake(self.hideView.center.x, -self.hideView.center.y);
self.innerScrollview.frame = CGRectMake(0, 0, UIScreenWidth, UIScreenHeight);
}];
}
}
else if(scrollView.contentOffset.y < triggerToHideY){
if (self.isHide) {
self.isHide = NO;
[UIView animateWithDuration:0.3 animations:^{
self.hideView.center = CGPointMake(self.hideView.center.x, -(self.hideView.center.y));
self.innerScrollview.frame = CGRectMake(0, titleHeight, UIScreenWidth, UIScreenHeight);
}];
}
}
}
这就是大概的效果图,对于动画的一些设置可以调整一下(动画时间啊,动画时间函数什么的),可能会有更好的效果。
工程的源代码已经上传到了Github上。由于本项目是使用了cocoapods进行第三方框架的引入,所以如果有问题的话可以考虑pod install
或pod update
一下。如果还有别的问题可以联系我。
项目为iOSDemon中的iOS_UI_study目录下的UIScrollviewAndHideView工程。
【传送门-iOSDemon-iOS_UI_study-UIScrollviewAndHideView】