用过 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;
}
复制代码
















