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

相关文章
|
开发者 iOS开发
iOS 源码分析(三):MLeaksFinder
iOS 源码分析(三):MLeaksFinder
696 0
iOS 源码分析(三):MLeaksFinder
|
调度 C++ iOS开发
IOS_多线程
IOS_多线程
98 0
|
设计模式 缓存 编解码
2020年iOS大厂面试题总结
2020年iOS大厂面试题总结
431 0
|
iOS开发
iOS 源码分析(一):CTMediator
iOS 源码分析(一):CTMediator
1266 0
iOS 源码分析(一):CTMediator
|
JSON 数据格式 iOS开发
iOS 源码分析(二):YYModel
iOS 源码分析(二):YYModel
568 0
iOS 源码分析(二):YYModel
|
iOS开发 MacOS
iOS-底层原理 01:源码探索的三种方式
iOS-底层原理 01:源码探索的三种方式
162 0
iOS-底层原理 01:源码探索的三种方式
|
iOS开发
IOS下P2P播放器开发如何实现?
IOS下P2P播放器开发如何实现?1)播放器在播放磁力链、或者torrrent文件时,调用自身的P2P引擎,p2p引擎里面通过torrent或者磁力链信息,去通过tracker、DHT寻找周围的peer邻居,然后获取数据。
2165 0
|
调度 iOS开发
|
存储 Java C语言