UIScrollView嵌套的完美解决方案
UIScrollView嵌套的完美解决方案
做iOS开发,不可避免的会遇到UIScrollView的嵌套问题,之前也曾遇到过,吭哧吭哧做完了,效果不理想,和产品大战好几回合,就那样了。不可避免的,又一次遇到了这个问题,就和同事一起研究了一下,彻底解决了这个问题。写了一个demo,以后再遇到就直接用了。今天主要是总结一下实现难点。免得自己过段时间又忘了,也给有同样困扰的你一个思路。
需求
如图:
要求:上滑的时候先滑headerView,headerView滑出屏幕时,tableView吸顶且开始滑动。下滑时先滑tableView,滑到顶部第一个cell出现,则开始滑headerView。 这是一个最简单的scrollView嵌套需求,后面还会有进阶的需求。
具体方案
其实嵌套最大的问题就是手势冲突问题,上层的ScrollView会拦截手势,导致手指在上层ScrollView滑动的时候,下层ScrollView不动。所以我们首先要让手势冲突时,两个手势都去响应。这样,我们滑动的时候,两个scrollView都会滑动。
第一步 上层scrollView不拦截手势
extension TopScrollView: UIGestureRecognizerDelegate {
//手势冲突的时候同时响应
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
第二步 创建上下文对象
上下两层scrollView滑动时候都需要对方的offset来计算,所以我们创建一个上下文对象,让两个scrollView都持有,避免了频繁正反向传值的问题。
class SyncScrollContext {
var maxOffsetY: CGFloat = 0 //上层最大的滑动距离
var outerOffset: CGPoint = CGPoint.zero //上层offset
var innerOffset: CGPoint = CGPoint.zero //下层offset
}
第三步 滑动的时候计算滑动优先级
下层scrollView的contentOffset变化时计算: ~~~ class BottomScrollView: UIScrollView {
class BottomScrollView: UIScrollView { var syncScrollContext: SyncScrollContext? override var contentOffset: CGPoint {
didSet {
if contentOffset.y != oldValue.y {
//下层scrollView滑动
if syncScrollContext.innerOffset.y > 0 {
// 上层的scrollView滑动,则下层的scrollView保持最大滑动距离
contentOffset.y = syncScrollContext.maxOffsetY
} else {
//否则,上层不动,下层滑动
}
//同步offset到上下文
syncScrollContext.outerOffset = contentOffset
}
}
}
}
上层的scrollView的contentOffset变化时计算:
class TopScrollView: UITableView { var syncScrollContext: SyncScrollContext? override var contentOffset: CGPoint {
didSet {
if contentOffset.y != oldValue.y {
//上层滑动
guard let syncScrollContext = syncScrollContext else { return }
if syncScrollContext.outerOffset.y < syncScrollContext.maxOffsetY {
//下层的offset < 下层可滑动最大值,说明下层还需要滑动,上层不动offset为0
contentOffset.y = 0
}
//不管怎么样,滑动即同步offset到上下文
syncScrollContext.innerOffset = contentOffset
}
}
}
}
第四步 两个ScrollView嵌套,并正确设置下层scrollView的contentSize
在下层BottomScrollView里面,添加topScrollView并设置contentSize。下层scrollView的contentSize的高 = headerView.height + topScrollView.height。这样,当下层scrollView滑了y(y = headerView的高度)的时候,下层scrollView滑到底了,这时候c下层scrollView无法滑动,也就不存在手势冲突,上层scrollView自动开始响应,流畅的滑动起来了
topScrollView.frame = CGRect(x: 0, y: offsetY, width: bounds.width, height: bounds.height) contentSize = CGSize(width: bounds.width, height: topScrollView.frame.maxY)
到这里,就已经大功告成了!demo下载:https://github.com/wangdachui/ScrollViewNested
进阶的需求
上下滑的同时,还要求左右滑:
具体就不多讲了,有兴趣看源码。 demo下载:https://github.com/wangdachui/TabScrollView
UIScrollView嵌套的完美解决方案的更多相关文章
- WebBrowser脚本错误的完美解决方案
原文:WebBrowser脚本错误的完美解决方案 当IE浏览器遇到脚本错误时浏览器,左下角会出现一个黄色图标,点击可以查看脚本错误的详细信息,并不会有弹出的错误信息框.当我们使用WebBrowse ...
- Apache服务器网站访问伪静态内页出现No input file specified.的完美解决方案
原文地址:Apache服务器网站访问伪静态内页出现No input file specified.的完美解决方案 启用REWRITE的伪静态功能的时候,首页可以访问,而访问内页的时候,就提示:&quo ...
- Atitit.异常处理 嵌套 冗长的解决方案
Atitit.异常处理 嵌套 冗长的解决方案 1. 异常处理的需要改进的地方1 2. +异常设计的初衷是, 在程序中出现错误时, 由程序自己处理错误, 尽量不要以exit(0)这种粗暴的方式中止程序 ...
- 关于Entity Framework中的Attached报错的完美解决方案终极版
之前发表过一篇文章题为<关于Entity Framework中的Attached报错的完美解决方案>,那篇文章确实能解决单个实体在进行更新.删除时Attached的报错,注意我这里说的单个 ...
- No resource found that matches the given name 'Theme.AppCompat.Light 的完美解决方案
No resource found that matches the given name 'Theme.AppCompat.Light 的完美解决方案 首先这个问题的产生是由于缺少Theme.App ...
- ecshop之transport和jquery冲突之完美解决方案
众所周知:ecshop的transport.js文件和Jquery是冲突的,两个文件不能同时调用,现给出以下完美解决方案:原因分析:在transport.js文件中,大概 580行到590行之间,这个 ...
- Xcode6.1标准Framework静态库制作方法。工程转Framework,静态库加xib和图片。完美解决方案。
http://www.cocoachina.com/bbs/read.php?tid-282490.html Xcode6.1标准Framework静态库制作方法.工程转Framework,静态库加x ...
- Safari 前端开发调试 iOS 完美解决方案
转http://www.2cto.com/kf/201403/283404.html afari 前端开发调试 iOS 完美解决方案 2014-03-05 0个评论 来源:Safari ...
- C#多线程解决界面卡死问题的完美解决方案
C#多线程解决界面卡死问题的完美解决方案 文章转自http://www.sufeinet.com/thread-3556-1-1.html 问题描述: 当我们的界面需要在程序运行中不断更新数据时, 当 ...
随机推荐
- 后缀数组的第X种求法
后缀自动机构造后缀数组. 因为有个SB题洛谷5115,它逼迫我学习后缀数组...(边分树合并是啥?). 一些定义:sa[i]表示字典序排第i的后缀是从哪里开始的.Rank[i]表示后缀i的排名.hei ...
- python中深拷贝和浅拷贝
python中所谓浅拷贝就是对引用的拷贝,所谓深拷贝就是对对象的资源的拷贝. 首先,对赋值操作我们要有以下认识: 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 ). 修改不可变 ...
- 工具类:Colletions ,Arrays(静态导入,可变参数,强循环)
一.Collecti 专门用来操作集合的工具类,没有构造函数,全静态方法. 常用方法: static <T extends Comparable<? super T>> voi ...
- mybatis的0和null
<if test="type!=null and type!=''">这样判断会把0也当做null的 另外写一个条件判断0 <if test="type ...
- hdu 4333"Revolving Digits"(KMP求字符串最小循环节+拓展KMP)
传送门 题意: 此题意很好理解,便不在此赘述: 题解: 解题思路:KMP求字符串最小循环节+拓展KMP ①首先,根据KMP求字符串最小循环节的算法求出字符串s的最小循环节的长度,记为 k: ②根据拓展 ...
- hystrix实战
https://blog.csdn.net/Ezreal_King/article/details/72942823
- (Prim算法)codeVs 1078 最小生成树
题目描述 Description 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场.当然,他需要你的帮助. 约翰已经给他的农场安排了一条高速的网络线路,他想把这 ...
- pt-archiver数据导入迁移工具
pt-archiver数据导入迁移工具 一直想明白,如何将一个大表的数据,每多少行数据已提交,分批次的转储到另外的地方,幸好有现成的工具,赶紧把实验成功的操作记录下来. 原理就不解释了,直接上最常用的 ...
- linux ssh 报错failed - POSSIBLE BREAK-IN ATTEMPT
linux ssh 报错failed - POSSIBLE BREAK-IN ATTEMPT 问题故障: 今天在新租的虚拟机上,发现ssh登陆机器的时候报错,如下: [root@pictures_ne ...
- 兄弟连Linux运维学习笔记
最新经典linux运维兄弟连Linux运维学习笔记... --------------- 全程1.5倍播放.加油我一定可以学完Linux----------------------Unix与Linux ...