用过 Airbnb 的都知道首页有一个 tableView
头部视图层叠效果, 就像这样的 :
实现起来很简单, 这里就来看一下怎么实现这个效果.
实现思路
- 先搭建一个tableView基础, 新建一个工程,把原有的
Storyboard
中的UIViewController
删除,拉一个UITableViewController
进来,并把is Initial View Controller
勾上, 把ViewController
的父类改成UITableViewController
, 最后把在Storyboard
中把UITableViewController
的Class
改成ViewController
,command + R
运行. - 接下来继承
UIView 创建一个带
Xib的自己的
HeaderView,在
HeaderView中使用
autolayout布局一个
UIImageView` 容器视图,拉入美女图片,显示. - 回到
ViewController
中,把刚才创建的HeaderView
作为一个子视图插入到tableView
下方.设置tableView
上部额外的滚动区域,用来显示头部视图. - 在
scrollViewDidScroll
方法中设置一个层叠的覆盖速率,保存想要的frame
, - 在
viewWillLayoutSubviews
方法中更新头部视图的frame
,command + R
, 搞定.
核心思路
在我们滚动的时候,计算好头部视图垂直方向上的变化,保存到 frame
中,在 viewWillLayoutSubviews
不断更新头部视图的 frame
.
具体代码分析
- 在
@interface
新建两个属性:
@interface ViewController ()/** header */@property(nonatomic, strong)HeaderView *header;/** headerFrame */@property(nonatomic, assign)CGRect headerFrame;@end复制代码
- 约定两个常量的值
// 头部视图高度const CGFloat headerHeight = 400;// 层叠的覆盖速率const CGFloat speed = 0.6; // speed <= 1 复制代码
- 在
viewDidLoad
方法里,设置头部视图的插入层级关系, 给tableView
顶部插入额外的滚动区域,用来显示头部视图, 应用启动, 滚动到最顶部,显示额外的头部视图.
- (void)viewDidLoad { [super viewDidLoad]; HeaderView *header = [HeaderView viewFromXib]; // 一定要将header插入到tableView的下面,才有层叠覆盖的效果 [self.view insertSubview:header atIndex:0]; self.header = header; // 给tableView顶部插入额外的滚动区域,用来显示头部视图 self.tableView.contentInset = UIEdgeInsetsMake(headerHeight, 0, 0, 0); // 应用启动, 滚动到最顶部,显示额外的头部视图 [self.tableView setContentOffset:CGPointMake(0, -headerHeight)]; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:reuseID];}复制代码
- 在
scrollViewDidScroll
方法里就是实现效果的核心代码, 计算headerView
以我们滚动时的速度的speed
倍速度滚动时对应的frame
值,并保存起来.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{ // 由于上面设置了应用额外的滚动区域,所以应用一启动就会来到这个方法 // scrollView.contentOffset.y的值是添加的额外滚动的值headerHeight, 此时headerHeight * speed - headerHeight * (1 - speed) = headerHeight, 从而一启动的时候, 头部视图是按照我们想要的位置布局的 // 以后当tableView滚动的时候, 头部视图就以我们滚动时的速度的speed倍滚动 , 把这个值保存到self.headerFrame, 在viewWillLayoutSubviews中更新头部视图的frame CGRect org = CGRectMake(0, -headerHeight, self.view.frame.size.width, headerHeight); org.origin.y = scrollView.contentOffset.y * speed - headerHeight * (1 - speed); self.headerFrame = org;}复制代码
- 在
viewWillLayoutSubviews
中不断更新头部视图的frame
.
-(void)viewWillLayoutSubviews{ [super viewWillLayoutSubviews]; // 每次布局子控件的时候,都要更新头部视图的frame self.header.frame = self.headerFrame;}复制代码