iOS-嵌套滚动

你是否也遇到过这个效果,嵌套滚动? 如何实现呢

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

对您有帮助的话,欢迎点点小心心~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容