zoukankan      html  css  js  c++  java
  • 新闻类App顶部菜单栏封装

    概述

    最近有一个需求,类似今日头条顶部的菜单栏。唯一区别是需要带可移动的下划线。网上查找资料,发现解决方案大部分是用UIScrollView实现。下方VC控制用UICollectionView。这样可以解决问题,但是不完美,当标签很多的时候,这时候的UIScrollView上会有大量写死的Button,没有达到复用的目的。所以自己封装了一个空间。菜单栏使用UICollectionView,VC控制使用PageViewController。

    这样做的目的是为了完全复用,支持无限扩展。因为菜单栏是collectionView,所以不怕内存爆掉。VC的控制使用PageViewController,好处是滑动的时候可以懒加载,只有用户浏览的时候才会实例化并缓存起来。网上的其他方案都是一次性把所有VC都实例化,然后使用CollectionView管理,这是不好的,因为有些VC用户可能从来不浏览,没必要实例化。

    接下来就详细介绍一下。

    实现难点

    1. 菜单栏需要把所选的一栏居中显示

      使用ScrollView,需要手动计算,设置offset,让其被选栏居中,比较麻烦。如果使用CollectionView,CollectionView有一个方法: 

      open func scrollToItem(at indexPath: IndexPath, at scrollPosition: UICollectionView.ScrollPosition, animated: Bool)

      只要将scrollPosition设置为.centeredHorizontally,即可实现该功能

    2. 左右滑动的时候,可以切换所选菜单,且下方横线需要跟着动

      使用ScrollView的话就比较方便了,计算滑动距离和屏幕宽的比例,让下划线跟着滑即可。但是使用CollectionView的话,滑完之后会自动居中显示被选菜单。位置就会出错。解决方案就是让下划线跟着被选菜单cell的位置。

      在collectionView中,滑动cell的时候其实只是offset在变,cell的frame其实是不变的,collectionView其实也是个ScrollView,cell是加在scrollView的contentView上的。在这里卡壳了好久。解决方案是,将cell的坐标转化到collectionView上,然后让下划线的中心点和cell在collectionView上中心点保持一致

       
      if let currentCell = collectionView.cellForItem(at: IndexPath(row: currentIndex, section: 0)) {
                  lineView.center.x = currentCell.convert(CGPoint(x: currentCell.bounds.width / 2.0, y: 0), to: scrollView).x
              }
    3. PageViewController没有ScrollView的Delegate,滑动的时候,不知道滑动的情况。

      可以使用一个暗黑技巧:

       
      for subview in pageViewController.view.subviews {
      	            if let scrollView = subview as? UIScrollView {
      	                scrollView.delegate = self
      	            }
      	        }
      

      然后再scrollViewDidScroll方法中操作

       
      public func scrollViewDidScroll(_ scrollView: UIScrollView) {
              if isForbideScroll { return}
              
              var progress:CGFloat = 0
              var nextIndex = 0
              let screenWidth = UIScreen.main.bounds.width
              let count = items.count
              
              //判断是左移还是右移
              if UIScreen.main.bounds.width > scrollView.contentOffset.x{    //右移动
                  nextIndex = currentIndex - 1
                  if nextIndex < 0 {
                      nextIndex = 0
                  }
                  //计算progress
                  progress = (screenWidth - scrollView.contentOffset.x)/screenWidth
              }
              if UIScreen.main.bounds.width < scrollView.contentOffset.x{    //左移
                  nextIndex = currentIndex + 1
                  if nextIndex > count - 1 {
                      nextIndex = count - 1
                  }
                  progress = (scrollView.contentOffset.x - screenWidth)/screenWidth
              }
              if progress != 0.0 {
                  topBar.pageViewScroll(nextIndex: nextIndex, progress: progress)
              }
          }
      

    如何使用

    1. 风格控制类SegmentTopBarStyle
    2. 数据源[SegmentItem]
    3. 自定义VC必须实现ChildViewControllerProtocol协议,协议中初始化方法可以按需修改,增加参数。初始化方法修改后记得在ScrollPageView中修改自定义VC的初始化。
    4. ScrollPageView中使用了SegmentTopBarView,所以你也可以单独使用SegmentTopBarView 
    override func viewDidLoad() {
            super.viewDidLoad()
            
            //风格控制
            let style = SegmentTopBarStyle()
            style.bottomLineColor = UIColor.red
            style.showExtraButton = false
            
            //datasource
            let items = dataSource()
            
            //ScrollPageView
            let subiView = ScrollPageView(frame: view.bounds,
                                          items: items,
                                          style: style,
                                          parentVC: self,
                                          customClassType: ChildViewController.self)
            view.addSubview(subiView)
        }
    
        func dataSource() -> [SegmentItem] {
            // 讲数据转化为SegmentItem的数组
            var arr = [SegmentItem]()
            for i in 0...10 {
                let item = SegmentItem(title: "title(i)", cid: "(i)")
                arr.append(item)
            }
            return arr
        }

    源码

    demo基于swift4.0。代码不多,稍微阅读下就能看懂。你也许会有更多的个性化的定制,可以在这个结构上随意改,拿走不谢

    demo: https://github.com/wangdachui/Segment

  • 相关阅读:
    不应滥用named let
    PAT甲级——A1106 Lowest Price in Supply Chain
    PAT甲级——A1105 Spiral Matrix【25】
    PAT甲级——A1104 Sum of Number Segments
    PAT甲级——A1103 Integer Factorization
    PAT甲级——A1102 Invert a Binary Tree
    PAT甲级——A1101 Quick Sort
    PAT甲级——A1100 Mars Numbers
    PAT甲级——A1099 Build A Binary Search Tree
    PAT甲级——A1098 Insertion or Heap Sort
  • 原文地址:https://www.cnblogs.com/6duxz/p/9953087.html
Copyright © 2011-2022 走看看