新闻类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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏林德熙的博客

WPF 修改图片颜色

在 WPF 可以使用很多图片处理的方法,本文告诉大家的是一个图片处理,可以把处理的图片保存在文件。

3351
来自专栏一“技”之长

iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用

        在iOS系统中,手势是进行用户交互的重要方式,通过UIGestureRecognizer类,我们可以轻松的创建出各种手势应用于app中。关于UI...

792
来自专栏python爬虫日记

python3下搜狗AI API实现

a、搜狗也发布了自己的人工智能 api,包括身份证ocr、名片ocr、文本翻译等API,初试感觉准确率一般般。

1793
来自专栏Flutter&Dart

Flutter之drawer详细分析(你要的操作都有)

可以看到,根据我们对drawer的认识,并不是想要的结果,所以这个drawer并不完整,然后我们继续添加代码,修改drawer

1972
来自专栏向治洪

MobX 在 React Native开发中的应用

MobX 是一款精准的状态管理工具库,如果你在 React 和 React Native 应用中使用过 Flux、Alt、Redux 和 Reflux,那毫不犹...

2597
来自专栏向治洪

MobX 在 React Native开发中的应用

MobX 是一款精准的状态管理工具库,如果你在 React 和 React Native 应用中使用过 Flux、Alt、Redux 和 Reflux,那毫不犹...

5048
来自专栏Alice

ios tableview 上加 textfiled

ios tableview 上加 textfiled  首先附上我项目中用曾经用到的几张图  并说明一下我的用法: 图1: ? 图2: ? 图3: ? 心在你我...

2255
来自专栏pangguoming

c#以POST方式模拟提交表单

这是我一年前写的一个用C#模拟以POST方式提交表单的代码,现在记录在下面,以免忘记咯。那时候刚学C#~忽忽。。很生疏。。代码看上去也很幼稚 臃肿不堪 #reg...

3239
来自专栏哲学驱动设计

OEA 中 WPF 树型表格整体重构

为什么要重构     上两个月主要做了一件事情,那就是把 OEA 框架中的 TreeGrid 控件,从结构上重新设计,并大量重构现有代码。而花较大精力做这件事的...

2046
来自专栏非著名程序员

基础篇章:关于 React Native 之 Navigator 组件的讲解

(友情提示:RN学习,从最基础的开始,大家不要嫌弃太基础,会的同学请自行略过,希望不要耽误已经会的同学的宝贵时间) 今天我们来讲讲Navigator这个小家伙,...

1907

扫码关注云+社区

领取腾讯云代金券