LTScrollView实现原理 - iOS

简介: LTScrollView实现原理 - iOS

image.png

  • 基础版实现思路

      1.1 层次结构

      底部是一个UITableView,上面黄色部分为tableView的tableHeaderView,cell的数 量为1,cell的contentView上放置了一个LTPageView, pageView上放置了一个scrollView且可以左右滑动分页,scrollView上放置控制器view,控制器view上放置各自的scrollView(tableView或collectionView)


     1.2 使用方法

   a.创建LTSimpleMabager,并添加到视图,传入frame,子控制器数组,标题数组,当前的控制器以及pageView的样式设置


LTSimpleManager(frame: <#T##CGRect#>, viewControllers: <#T##[UIViewController]#>, titles: <#T##[String]#>, currentViewController: <#T##UIViewController#>, layout: <#T##LTLayout#>)

 

   b.初始化子控制器的scrollView(tableView或collectionView),且tableView的y值从pageTitleView的高度开始,Demo中为44,具体可根据产品需求而定,tableView的height则为父view的高减去44


  c.将子控制器的scrollView(tableView或collectionView)赋值给glt_scollView即glt_scrollView = tableView,以下会说明原因。


   1.3 实现思路

   a.当滑动底部tableView的时候,当tableView的contentOffset.y 小于 header的高的时候,将内容ScrollView的contentOffset设置为.zero













private func contentScrollViewScrollConfig(_ viewController: UIViewController) {        viewController.glt_scrollView?.scrollHandle = {[weak self] scrollView in            guard let `self` = self else { return }            self.contentTableView = scrollView            if self.tableView.contentOffset.y < self.kHeaderHeight {                scrollView.contentOffset = .zero;                scrollView.showsVerticalScrollIndicator = false            }else{                scrollView.showsVerticalScrollIndicator = true            }        }    }

   

   b.当滑动内容ScrollView的时候, 当内容contentOffset.y 大于 0(说明滑动的是内容ScrollView) 或者 当底部tableview的contentOffset.y大于 header的高度的时候,将底部tableView的偏移量设置为kHeaderHeight, 并将其他的scrollView的contentOffset置为.zero














public func scrollViewDidScroll(_ scrollView: UIScrollView) {        guard scrollView == tableView, let contentTableView = contentTableView else { return }        let offsetY = scrollView.contentOffset.y        if contentTableView.contentOffset.y > 0.0 || offsetY > kHeaderHeight {            tableView.contentOffset = CGPoint(x: 0.0, y: kHeaderHeight)        }        if scrollView.contentOffset.y < kHeaderHeight {            for viewController in viewControllers {                guard viewController.glt_scrollView != scrollView else { continue }                viewController.glt_scrollView?.contentOffset = .zero            }        }    }

   

   c.headerView添加以及各个点击事件回调处理






simpleManager.configHeaderView {[weak self] in            guard let strongSelf = self else { return nil }            let headerView = strongSelf.testLabel()            return headerView        }



simpleManager.didSelectIndexHandle { (index) in                   }







simpleManager.refreshTableViewHandle { (scrollView, index) in            scrollView.mj_header = MJRefreshNormalHeader {                DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {                    scrollView.mj_header.endRefreshing()                })            }        }


  • 进阶版实现思路

   2.1 层次结构

   底部是LTPageView, pageView上放置了一个scrollView且可以左右滑动分页,scrollView上放置控制器view,headerView是一个单独的View在顶部,利用scrollView的contentInset将其放置在了最上面


   2.2 使用方法

   a.创建LTAdvancedManager,并添加到视图,传入frame,子控制器数组,标题数组,当前的控制器以及pageView的样式设置和自己的headerView(在闭包中返回即可)


LTAdvancedManager(frame: <#T##CGRect#>, viewControllers: <#T##[UIViewController]#>, titles: <#T##[String]#>, currentViewController: <#T##UIViewController#>, layout: <#T##LTLayout#>, headerViewHandle: <#T##() -> UIView#>)

   

   b.初始化子控制器的scrollView(tableView或collectionView),且tableView的y值从0开始,tableView的height则为父view的高减去44

   

   c.将子控制器的的scrollView(tableView或collectionView)赋值给glt_scollView即glt_scrollView = tableView,以下会说明原因


   2.3 实现思路

   主要是利用子控制的滚动来控制headerView

















































//MARK: 当前控制器的滑动方法事件处理     private func contentScrollViewDidScroll(_ contentScrollView: UIScrollView, _ absOffset: CGFloat)  {                //获取当前控制器        let currentVc = viewControllers[currentSelectIndex]                //外部监听当前ScrollView的偏移量        self.delegate?.glt_scrollViewOffsetY?((currentVc.glt_scrollView?.contentOffset.y ?? kHeaderHeight) + self.kHeaderHeight + layout.sliderHeight)                //获取偏移量        let offsetY = contentScrollView.contentOffset.y                //获取当前pageTitleView的Y值        var pageTitleViewY = pageView.pageTitleView.frame.origin.y                //pageTitleView从初始位置上升的距离        let titleViewBottomDistance = offsetY + kHeaderHeight + layout.sliderHeight                let headerViewOffset = titleViewBottomDistance + pageTitleViewY                if absOffset > 0 && titleViewBottomDistance > 0 {//向上滑动            if headerViewOffset >= kHeaderHeight {                pageTitleViewY += -absOffset                if pageTitleViewY <= hoverY {                    pageTitleViewY = hoverY                }            }        }else{//向下滑动            if headerViewOffset < kHeaderHeight {                pageTitleViewY = -titleViewBottomDistance + kHeaderHeight                if pageTitleViewY >= kHeaderHeight {                    pageTitleViewY = kHeaderHeight                }            }        }                pageView.pageTitleView.frame.origin.y = pageTitleViewY        headerView?.frame.origin.y = pageTitleViewY - kHeaderHeight        let lastDiffTitleToNavOffset = pageTitleViewY - lastDiffTitleToNav        lastDiffTitleToNav = pageTitleViewY        //使其他控制器跟随改变        for subVC in viewControllers {            guard subVC != currentVc else { continue }            guard let vcGlt_scrollView = subVC.glt_scrollView else { continue }            vcGlt_scrollView.contentOffset.y += (-lastDiffTitleToNavOffset)            subVC.glt_upOffset = String(describing: vcGlt_scrollView.contentOffset.y)        }    }

源码:https://github.com/gltwy/LTScrollView

  • 基础版实现思路

      1.1 层次结构

      底部是一个UITableView,上面黄色部分为tableView的tableHeaderView,cell的数 量为1,cell的contentView上放置了一个LTPageView, pageView上放置了一个scrollView且可以左右滑动分页,scrollView上放置控制器view,控制器view上放置各自的scrollView(tableView或collectionView)


     1.2 使用方法

   a.创建LTSimpleMabager,并添加到视图,传入frame,子控制器数组,标题数组,当前的控制器以及pageView的样式设置


LTSimpleManager(frame: <#T##CGRect#>, viewControllers: <#T##[UIViewController]#>, titles: <#T##[String]#>, currentViewController: <#T##UIViewController#>, layout: <#T##LTLayout#>)

 

   b.初始化子控制器的scrollView(tableView或collectionView),且tableView的y值从pageTitleView的高度开始,Demo中为44,具体可根据产品需求而定,tableView的height则为父view的高减去44


  c.将子控制器的scrollView(tableView或collectionView)赋值给glt_scollView即glt_scrollView = tableView,以下会说明原因。


   1.3 实现思路

   a.当滑动底部tableView的时候,当tableView的contentOffset.y 小于 header的高的时候,将内容ScrollView的contentOffset设置为.zero

private func contentScrollViewScrollConfig(_ viewController: UIViewController) {
        viewController.glt_scrollView?.scrollHandle = {[weak self] scrollView in
            guard let `self` = self else { return }
            self.contentTableView = scrollView
            if self.tableView.contentOffset.y < self.kHeaderHeight {
                scrollView.contentOffset = .zero;
                scrollView.showsVerticalScrollIndicator = false
            }else{
                scrollView.showsVerticalScrollIndicator = true
            }
        }
    }
private func contentScrollViewScrollConfig(_ viewController: UIViewController) {
        viewController.glt_scrollView?.scrollHandle = {[weak self] scrollView in
            guard let `self` = self else { return }
            self.contentTableView = scrollView
            if self.tableView.contentOffset.y < self.kHeaderHeight {
                scrollView.contentOffset = .zero;
                scrollView.showsVerticalScrollIndicator = false
            }else{
                scrollView.showsVerticalScrollIndicator = true
            }
        }
    }

   

   b.当滑动内容ScrollView的时候, 当内容contentOffset.y 大于 0(说明滑动的是内容ScrollView) 或者 当底部tableview的contentOffset.y大于 header的高度的时候,将底部tableView的偏移量设置为kHeaderHeight, 并将其他的scrollView的contentOffset置为.zero



public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard scrollView == tableView, let contentTableView = contentTableView else { return }
        let offsetY = scrollView.contentOffset.y
        if contentTableView.contentOffset.y > 0.0 || offsetY > kHeaderHeight {
            tableView.contentOffset = CGPoint(x: 0.0, y: kHeaderHeight)
        }
        if scrollView.contentOffset.y < kHeaderHeight {
            for viewController in viewControllers {
                guard viewController.glt_scrollView != scrollView else { continue }
                viewController.glt_scrollView?.contentOffset = .zero
            }
        }
    }
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard scrollView == tableView, let contentTableView = contentTableView else { return }
        let offsetY = scrollView.contentOffset.y
        if contentTableView.contentOffset.y > 0.0 || offsetY > kHeaderHeight {
            tableView.contentOffset = CGPoint(x: 0.0, y: kHeaderHeight)
        }
        if scrollView.contentOffset.y < kHeaderHeight {
            for viewController in viewControllers {
                guard viewController.glt_scrollView != scrollView else { continue }
                viewController.glt_scrollView?.contentOffset = .zero
            }
        }
    }

   

   c.headerView添加以及各个点击事件回调处理


simpleManager.configHeaderView {[weak self] in
            guard let strongSelf = self else { return nil }
            let headerView = strongSelf.testLabel()
            return headerView
        }
simpleManager.configHeaderView {[weak self] in
            guard let strongSelf = self else { return nil }
            let headerView = strongSelf.testLabel()
            return headerView
        }



simpleManager.didSelectIndexHandle { (index) in
        }
simpleManager.didSelectIndexHandle { (index) in
        }



simpleManager.refreshTableViewHandle { (scrollView, index) in
            scrollView.mj_header = MJRefreshNormalHeader {
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
                    scrollView.mj_header.endRefreshing()
                })
            }
        }simpleManager.refreshTableViewHandle { (scrollView, index) in
            scrollView.mj_header = MJRefreshNormalHeader {
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
                    scrollView.mj_header.endRefreshing()
                })
            }
        }


  • 进阶版实现思路

   2.1 层次结构

   底部是LTPageView, pageView上放置了一个scrollView且可以左右滑动分页,scrollView上放置控制器view,headerView是一个单独的View在顶部,利用scrollView的contentInset将其放置在了最上面

LTAdvancedManager(frame: <#T##CGRect#>, viewControllers: <#T##[UIViewController]#>, titles: <#T##[String]#>, currentViewController: <#T##UIViewController#>, layout: <#T##LTLayout#>, headerViewHandle: <#T##() -> UIView#>)LTAdvancedManager(frame: <#T##CGRect#>, viewControllers: <#T##[UIViewController]#>, titles: <#T##[String]#>, currentViewController: <#T##UIViewController#>, layout: <#T##LTLayout#>, headerViewHandle: <#T##() -> UIView#>)

   2.2 使用方法

   a.创建LTAdvancedManager,并添加到视图,传入frame,子控制器数组,标题数组,当前的控制器以及pageView的样式设置和自己的headerView(在闭包中返回即可)


//MARK: 当前控制器的滑动方法事件处理 
    private func contentScrollViewDidScroll(_ contentScrollView: UIScrollView, _ absOffset: CGFloat)  {
        //获取当前控制器
        let currentVc = viewControllers[currentSelectIndex]
        //外部监听当前ScrollView的偏移量
        self.delegate?.glt_scrollViewOffsetY?((currentVc.glt_scrollView?.contentOffset.y ?? kHeaderHeight) + self.kHeaderHeight + layout.sliderHeight)
        //获取偏移量
        let offsetY = contentScrollView.contentOffset.y
        //获取当前pageTitleView的Y值
        var pageTitleViewY = pageView.pageTitleView.frame.origin.y
        //pageTitleView从初始位置上升的距离
        let titleViewBottomDistance = offsetY + kHeaderHeight + layout.sliderHeight
        let headerViewOffset = titleViewBottomDistance + pageTitleViewY
        if absOffset > 0 && titleViewBottomDistance > 0 {//向上滑动
            if headerViewOffset >= kHeaderHeight {
                pageTitleViewY += -absOffset
                if pageTitleViewY <= hoverY {
                    pageTitleViewY = hoverY
                }
            }
        }else{//向下滑动
            if headerViewOffset < kHeaderHeight {
                pageTitleViewY = -titleViewBottomDistance + kHeaderHeight
                if pageTitleViewY >= kHeaderHeight {
                    pageTitleViewY = kHeaderHeight
                }
            }
        }
        pageView.pageTitleView.frame.origin.y = pageTitleViewY
        headerView?.frame.origin.y = pageTitleViewY - kHeaderHeight
        let lastDiffTitleToNavOffset = pageTitleViewY - lastDiffTitleToNav
        lastDiffTitleToNav = pageTitleViewY
        //使其他控制器跟随改变
        for subVC in viewControllers {
            guard subVC != currentVc else { continue }
            guard let vcGlt_scrollView = subVC.glt_scrollView else { continue }
            vcGlt_scrollView.contentOffset.y += (-lastDiffTitleToNavOffset)
            subVC.glt_upOffset = String(describing: vcGlt_scrollView.contentOffset.y)
        }
    }

   

   b.初始化子控制器的scrollView(tableView或collectionView),且tableView的y值从0开始,tableView的height则为父view的高减去44

   

   c.将子控制器的的scrollView(tableView或collectionView)赋值给glt_scollView即glt_scrollView = tableView,以下会说明原因


   2.3 实现思路

   主要是利用子控制的滚动来控制headerView

















































//MARK: 当前控制器的滑动方法事件处理     private func contentScrollViewDidScroll(_ contentScrollView: UIScrollView, _ absOffset: CGFloat)  {                //获取当前控制器        let currentVc = viewControllers[currentSelectIndex]                //外部监听当前ScrollView的偏移量        self.delegate?.glt_scrollViewOffsetY?((currentVc.glt_scrollView?.contentOffset.y ?? kHeaderHeight) + self.kHeaderHeight + layout.sliderHeight)                //获取偏移量        let offsetY = contentScrollView.contentOffset.y                //获取当前pageTitleView的Y值        var pageTitleViewY = pageView.pageTitleView.frame.origin.y                //pageTitleView从初始位置上升的距离        let titleViewBottomDistance = offsetY + kHeaderHeight + layout.sliderHeight                let headerViewOffset = titleViewBottomDistance + pageTitleViewY                if absOffset > 0 && titleViewBottomDistance > 0 {//向上滑动            if headerViewOffset >= kHeaderHeight {                pageTitleViewY += -absOffset                if pageTitleViewY <= hoverY {                    pageTitleViewY = hoverY                }            }        }else{//向下滑动            if headerViewOffset < kHeaderHeight {                pageTitleViewY = -titleViewBottomDistance + kHeaderHeight                if pageTitleViewY >= kHeaderHeight {                    pageTitleViewY = kHeaderHeight                }            }        }                pageView.pageTitleView.frame.origin.y = pageTitleViewY        headerView?.frame.origin.y = pageTitleViewY - kHeaderHeight        let lastDiffTitleToNavOffset = pageTitleViewY - lastDiffTitleToNav        lastDiffTitleToNav = pageTitleViewY        //使其他控制器跟随改变        for subVC in viewControllers {            guard subVC != currentVc else { continue }            guard let vcGlt_scrollView = subVC.glt_scrollView else { continue }            vcGlt_scrollView.contentOffset.y += (-lastDiffTitleToNavOffset)            subVC.glt_upOffset = String(describing: vcGlt_scrollView.contentOffset.y)        }    }

源码:https://github.com/gltwy/LTScrollView

目录
打赏
0
0
0
0
3
分享
相关文章
如何解决Swift混编的module编译错误
前言很多iOS工程都是基于Object-C开发,再逐步向Swift演进,演进过程中不可避免要进行Swift混编。Swift模块需要支持LLVM Module规范,混编工程会遇到各种Module编译错误。这对于不熟悉的同学来说简直是灾难,严重影响开发效率。本文会介绍常见的Module编译错误,希望对大家有所帮助。常见错误1:Could not build module xxx当一个OC模块引用了Sw
7279 1
如何解决Swift混编的module编译错误
WKWebView 加载 http:// *** 报错WebPageProxy::didFailProvisionalLoadForFrame:
WKWebView 加载 http:// *** 报错WebPageProxy::didFailProvisionalLoadForFrame:
3048 0
一张图的七十二变——阿里云OSS图片处理实践
      小张是某视频网站的新入职的UED,日常工作就是创作各式各样的海报banner。踌躇满志的小张,上了三天班就蔫了。因为他在完成一张图的创作后,还需要考虑:• 同一张图会以不同的形式应用于网站各处:有时候需裁剪成不同形状,有时需要加水印,有时需转换格式....• 为了风格统一,不同的图需要保持样式统一:不同图片排列组成成一组,每组图片风格(
2617 0
如何解决UICollectionView不能下拉刷新问题
如何解决UICollectionView不能下拉刷新问题
207 0
GitHub 支持双因素认证(2FA)
【9月更文挑战第29天】
1311 6
Flutter-底部弹出框(Widget层级)
文章描述了如何在Flutter中使用DraggableScrollableSheet创建一个底部弹出框,同时保持其可手势滑动关闭。作者遇到问题并提出对原控件进行扩展,以支持头部和列表布局的滑动关闭功能。
299 0
RxSwift操作符操作符map、flatMap、flatMapLatest、filter的使用与区别
RxSwift操作符操作符map、flatMap、flatMapLatest、filter的使用与区别
655 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问