你是否也遇到過這個效果,嵌套滾動? 如何實現(xiàn)呢
image.gif\
首先,我們知道,這一定一個嵌套滾動的問題.
基礎(chǔ)的頁面搭建不再贅述,直接闡述解決辦法.
通過手勢穿透,即讓一個滑動手勢既作用于底層的ScrollView又能作用于上層的業(yè)務(wù)tableView,同時控制他們的滾動即可達成目的松申。
- 比如說外層是一個TableView,我們讓外層這個TableView實現(xiàn)UIGestureRecognizerDelegate的這個shouldRecognizeSimultaneouslyWith方法,當滾動的是tag為100的view時,返回true,也就是這個view也能響應(yīng)到滾動事件
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
}
}
- 然后內(nèi)層的tablView設(shè)置一個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的滾動事件是都可以監(jiān)聽到的;然后通過控制兩個內(nèi)外兩個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)
}
}
}
}
外層的監(jiān)聽 及 響應(yīng)
//建立通知中心 監(jiān)聽離開置頂命令
NotificationCenter.default.addObserver(self, selector: #selector(acceptMsg(notification:)), name: Notification.Name.leaveTopNotificationName, object: nil)
//建立通知中心 監(jiān)聽進入置頂命令
NotificationCenter.default.addObserver(self, selector: #selector(acceptMsg(notification:)), name: Notification.Name.goTopNotificationName, object: nil)
/// 接收到通知的回調(diào)
///
/// - Parameter notification:
@objc func acceptMsg(notification: Notification) {
let notificationName = notification.name
if notificationName == Notification.Name.goTopNotificationName {//到達已經(jīng)吸頂部分
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
}
}
內(nèi)層的滾動
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)")
}
}
內(nèi)層的監(jiān)聽 及 響應(yīng)
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)
/// 接收到通知的回調(diào)
///
/// - 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
對您有幫助的話,歡迎點點小心心~