需求见图
就是这个简单需求,做了我两天
当然也有沟通不当的原因,
原来做的是外面的背景图那部分也要响应,因此采用了外部UIScrollView+内部UITableView(非原生UITableView)的设计
后面改成了只有内部列表需要响应,因此采用了外部View响应内部UITableView滑动事件改变显示的设计
因为是记录文档,因此主要记录一下遇到的坑,实现思路就只稍微提一下。
实现思路
第一种设计:
内部UITableView的高度固定,外部ScrollView的contentSize为HeaderImage+TableVIew-offset
这样确保外部可以达到响应滑动事件展示完整列表的要求。
但是会遇到一个圆角的问题,直接设置TableView的圆角将达不到设计要求,因为实际上TableView会被ScrollView截断,下部的圆角会显示不全
解决的方法是在ScrollView上面再加一个切圆角的View,这个View就需要响应两个滑动View的事件,进行frame的改变。
第二种设计:
外部是一个View,内部的TableView控制了整个视图变化。注意两者其实在同一级是并列而非包含关系。
当内部滑动时,只需要改变自身frame和外部View的frame就能实现同时滑动的效果。
上一下关键代码意思一下
CGPoint lastScroll;
CGPoint currScroll;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
currScroll = scrollView.contentOffset;
if(currScroll.y > lastScroll.y){ //上拉往下
if(self.curContentSizeY + (currScroll.y - lastScroll.y) < CGRectGetHeight(self.headerImageView.frame) - 15) {//背景View还能往上移动
CGRect frame = self.backgroundView.frame;
frame.origin.y = self.backgroundView.frame.origin.y - (currScroll.y - lastScroll.y);
frame.size.height = self.backgroundView.frame.size.height + (currScroll.y - lastScroll.y);
self.backgroundView.frame = frame;
self.curContentSizeY = self.curContentSizeY + (currScroll.y - lastScroll.y);
}
else {
CGRect frame = self.backgroundView.frame;
frame.origin.y = 0- (CGRectGetHeight(self.headerImageView.frame) - 15);
frame.size.height = CGRectGetMinY(self.myView.frame) - 12 + CGRectGetHeight(self.headerImageView.frame) - 15;
self.backgroundView.frame = frame;
self.curContentSizeY = CGRectGetHeight(self.headerImageView.frame) - 15;
}
}
else{ //下拉往上
if(self.curContentSizeY > (lastScroll.y - currScroll.y)){ // 背景view还能往下移动
CGRect frame = self.backgroundView.frame;
frame.origin.y = self.backgroundView.frame.origin.y + (lastScroll.y - currScroll.y);
frame.size.height = self.backgroundView.frame.size.height - (lastScroll.y - currScroll.y);
self.backgroundView.frame = frame;
self.curContentSizeY = self.curContentSizeY - (lastScroll.y - currScroll.y);
}
else{
CGRect frame = self.backgroundView.frame;
frame.origin.y = 0;
frame.size.height = CGRectGetMinY(self.myView.frame) - 12;
self.backgroundView.frame = frame;
self.curContentSizeY = 0;
}
}
CGRect frame = self.tableView.frame;
frame.origin.y = self.backgroundView.frame.origin.y + self.headerImageView.frame.size.height - 15;
frame.size.height = self.backgroundView.frame.size.height - self.headerImageView.frame.size.height + 15;
self.tableView.frame = frame;
lastScroll = currScroll;
[super scrollViewDidScroll:scrollView];
}
遇到的坑
主要是第一种设计的各种坑,浪费了许多时间,大概有三四天。
第二种实现一个早上不到就写好了,当然二者有共通的坐标处理,也不确切说一个早上就能完全写好。
1.事件传递与滑动事件
这个是ScrollView嵌套的坑(TableView本质也是一个ScrollView),最大的问题在于当内部TableView滑到底部后,滑动事件就会只由外部响应,导致内部无法实现上拉加载更多。
具体情况是通过htiTest可以确定触摸事件是已经传到了内部,但是滑动事件却不能触发,触摸与滑动之间发生了什么也不清楚。
2.delegate的覆盖
这个导致了内部TableView的刷新/加载表现和其他TableView不一样。因为已经扩展过了UIScrollView的各种滑动代理事件方法,因此在外层再次实现会把原有方法覆盖。
3.bounce的问题。TableView不是原生而是公司前辈扩展的,支持上拉加载和下拉刷新,但是把Bounce关闭就会失效。
这次需求需要用到上拉加载更多,这就导致了一个问题,bounce时也会触发滑动的回调scrollViewDidScroll,因此也会影响到外部的UI,会稍显不自然。这个也不算坑,但是如果有办法判断scrollViewDidScroll究竟是由于主动的拉动还是被动的回弹那就是最好的了。