你是否也遇到过这个效果,嵌套滚动? 如何实现呢
image.gif\
首先,我们知道,这一定一个嵌套滚动的问题.
基础的页面搭建不再赘述,直接阐述解决办法.
通过手势穿透,即让一个滑动手势既作用于底层的ScrollView又能作用于上层的业务tableView,同时控制他们的滚动即可达成目的。
- 比如说外层是一个TableView,我们让外层这个TableView实现UIGestureRecognizerDelegate的这个shouldRecognizeSimultaneouslyWith方法,当滚动的是tag为100的view时,返回true,也就是这个view也能响应到滚动事件
class TKGestureTableView: UITableView, UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if let otherView = otherGestureRecognizer.view {
if otherView.tag == 100 {
return true
}
}
return false
}
}
- 然后内层的tablView设置一个tag = 100
lazy var listView:UITableView = {
let tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = UITableView.automaticDimension
tableView.register(InnerNormalCell.classForCoder(), forCellReuseIdentifier: "InnerNormalCell")
tableView.estimatedRowHeight = 40
tableView.tag = 100
return tableView
}()
- 这样,当用户滑动底部的时候,两个view的滚动事件是都可以监听到的;然后通过控制两个内外两个View的滚动事件,完成上面的gif效果
外层的滚动
var isTopAndCanNotMoveTabViewPre:Bool = false
var isTopAndCanNotMoveTabView:Bool = false
var canScroll:Bool = false
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("out---\(scrollView.contentOffset)")
///吸顶效果
let offsetY = scrollView.contentOffset.y
let tabOffsetY:CGFloat = CGFloat(Int(listView.rectForRow(at: IndexPath(row: 4, section: 0)).origin.y))
isTopAndCanNotMoveTabViewPre = isTopAndCanNotMoveTabView
if offsetY >= tabOffsetY {
scrollView.contentOffset = CGPoint(x: 0, y: tabOffsetY)
isTopAndCanNotMoveTabView = true
}else {
isTopAndCanNotMoveTabView = false
}
if (isTopAndCanNotMoveTabView != isTopAndCanNotMoveTabViewPre){
if (!isTopAndCanNotMoveTabViewPre && isTopAndCanNotMoveTabView) {
//上滑-滑动到顶端
print("out---上滑-滑动到顶端--\(scrollView.contentOffset)")
NotificationCenter.default.post(name: Notification.Name.goTopNotificationName, object: nil, userInfo: ["canScroll":"1"])
canScroll = false
}
if(isTopAndCanNotMoveTabViewPre && !isTopAndCanNotMoveTabView){
//下滑-离开顶端
print("out---下滑-离开顶端--\(scrollView.contentOffset)")
if (!canScroll) {
scrollView.contentOffset = CGPoint(x: 0, y: tabOffsetY)
}
}
}
}
外层的监听 及 响应
//建立通知中心 监听离开置顶命令
NotificationCenter.default.addObserver(self, selector: #selector(acceptMsg(notification:)), name: Notification.Name.leaveTopNotificationName, object: nil)
//建立通知中心 监听进入置顶命令
NotificationCenter.default.addObserver(self, selector: #selector(acceptMsg(notification:)), name: Notification.Name.goTopNotificationName, object: nil)
/// 接收到通知的回调
///
/// - Parameter notification:
@objc func acceptMsg(notification: Notification) {
let notificationName = notification.name
if notificationName == Notification.Name.goTopNotificationName {//到达已经吸顶部分
if let canScroll_str = notification.userInfo?["canScroll"] as? String {
if canScroll_str == "1" {
canScroll = false
}
}
}else if notificationName == Notification.Name.leaveTopNotificationName {//离开吸顶部分
listView.contentOffset = CGPoint.zero
canScroll = true
}
}
内层的滚动
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (!InnerScrollerViewController.canScroll) {
scrollView.contentOffset = CGPoint.zero
}
let offsetY = scrollView.contentOffset.y
if (offsetY < 0) {
print("inner---下拉--\(scrollView.contentOffset)")
NotificationCenter.default.post(name: Notification.Name.leaveTopNotificationName, object: nil, userInfo: ["canScroll":"1"])
}else{
print("inner---上滑--\(scrollView.contentOffset)")
}
}
内层的监听 及 响应
NotificationCenter.default.addObserver(self, selector: #selector(acceptMsg(notification:)), name: Notification.Name.leaveTopNotificationName, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(acceptMsg(notification:)), name: Notification.Name.goTopNotificationName, object: nil)
/// 接收到通知的回调
///
/// - Parameter notification:
@objc func acceptMsg(notification: Notification) {
let notificationName = notification.name
if notificationName == Notification.Name.goTopNotificationName {
if let canScroll_str = notification.userInfo?["canScroll"] as? String {
if canScroll_str == "1" {
InnerScrollerViewController.canScroll = true
}
}
}else if notificationName == Notification.Name.leaveTopNotificationName {
listView.contentOffset = CGPoint.zero
InnerScrollerViewController.canScroll = false
}
}
详见:
https://github.com/bjduhuan/NestedScrollingTest.git
对您有帮助的话,欢迎点点小心心~