iOS ScrollView嵌套ScrolloView解决方案 - Swift

iOS ScrollView嵌套ScrolloView解决方案 - Swift

 
 
 

1. 基础版实现思路

1.1:层次结构

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

1.2:使用方法(具体查看Demo)

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

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

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

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

1.3:实现思路

(1)当滑动底部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
            }
        }
    }

(2)当滑动内容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
            }
        }
    }

 

(3)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.进阶版实现思路

2.1:层次结构

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

2.2:使用方法(具体查看Demo)

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

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

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

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

2.3:实现思路

(1)主要是利用子控制的滚动来控制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)
        }
    }

 

3.问题以及补充

 

3.1:问题1:全局监听scrollView的滚动

实现思路是根据runtime,交换方法的实现,主要的坑就是当时采用的交换scrollViewDidScroll(_:),但它是UIScrollViewDelegate的协议方法,发现行不通,后来找到了UIScrollView中的一个方法Selector(("_notifyDidScroll")),发现拦截它是可行的,当然Swift中拦截方法,也是有很多坑的,和OC有很大不同,具体操作看源码吧!

3.2:进阶版遗留小问题

当headerView中有按钮需要响应事件的时候,我这里采用的是利用override func point(inside point: CGPoint, with event: UIEvent?) -> Bool方法进行判断,因为上面headerView的交互只有关闭了才能滑动headerView左右切换,所以在这里我只允许要响应事件的控件交互打开了,其他区域交互关闭。如果有更好的思路欢迎联系!!!不胜感激!

//MARK: 暂用,待优化。
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        for tempView in self.subviews {
            if tempView.isKind(of: UILabel.self) {
                let button = tempView as! UILabel
                let newPoint = self.convert(point, to: button)
                if button.bounds.contains(newPoint) {
                    return true
                }
            }
        }
        return false
    }

 

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 42
    评论
作者gltwy,代码LTScrollView-master,LTScrollView-master能够无限滚动,可以设定视图出现的数量,总数量,实现无限滚动。通过协议方法和数据源定制自己的滚动视图。 Demo文件路径以及说明 LTScrollView / Example : 为 Swift 使用示例. LTScrollView / OCExample : 为 OC 使用示例. 支持的子ViewUIScrollView、UICollectionView、UITableView. CocoaPods安装 安装CocoaPods 使用以下命令: $ gem install cocoapods Podfile 在你的 Podfile中添加LTScrollView source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'TargetName' do #注意此处需要添加use_frameworks! use_frameworks! pod 'LTScrollView', '~> 0.1.7' end 然后,使用以下命令安装 $ pod install 提示错误 [!] Unable to find a specification for LTScrollView 解决办法: $ pod repo remove master $ pod setup Swift使用说明 Swift.LTSimple使用说明 创建LTSimpleManager实例对象 @objc public init(frame: CGRect, viewControllers: [UIViewController], titles: [String], currentViewController:UIViewController, layout: LTLayout) 设置headerView @objc public func configHeaderView(_ handle: (() -> UIView?)?) 子控制器中glt_scrollView进行赋值 self.glt_scrollView = self.tableView(self.scrollView / self.collectionView) 更多使用说明请参考Demo(LTScrollView / Example) Swift.LTAdvanced使用说明 创建LTAdvancedManager实例对象、并设置headerView @objc public init(frame: CGRect, viewControllers: [UIViewController], titles: [String], currentViewController:UIViewController, layout: LTLayout, headerViewHandle handle: () -> UIView) 子控制器中glt_scrollView进行赋值 self.glt_scrollView = self.tableView(self.scrollView / self.collectionView) 更多使用说明请参考Demo(LTScrollView / Example) OC使用说明 OC.LTSimple使用说明 创建LTSimpleManager实例对象 [[LTSimpleManager alloc] initWithFrame:frame viewControllers:self.viewControllers titles:self.titles currentViewController:self layout:self.layout] 设置headerView [self.managerView configHeaderView:^UIView * _Nullable{ }] 子控制器中glt_scrollView进行赋值 self.glt_scrollView = self.tableView(self.scrollView / self.collectionView) 更多使用说明请参考Demo(LTScrollView / OCExample) OC.LTAdvanced使用说明 创建LTAdvancedManager实例对象、并设置headerView [[LTAdvancedManager alloc] initWithFrame:frame viewControllers:self.viewControllers titles:self.titles

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 42
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值