概述

最近有一个需求,类似今日头条顶部的菜单栏。唯一区别是需要带可移动的下划线。网上查找资料,发现解决方案大部分是用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

新闻类App顶部菜单栏封装的更多相关文章

  1. 新闻类App使用的组件

    UI SlidingMenu:com.jeremyfeinstein.slidingmenu:滑动菜单 ActionBarSherlock:com.actionbarsherlock:Action B ...

  2. iOS开发之资讯类App常用分类控件的封装与实现(CollectionView+Swift3.0+)

    今天博客中,我们就来实现一下一些常用资讯类App中常用的分类选择的控件的封装.本篇博客中没有使用到什么新的技术点,如果非得说用到了什么新的技术点的话,那么勉强的说,用到了一些iOS9以后UIColle ...

  3. 分享一下怎么开发一款图片视频类App,秒拍和prisma

    第一步,分解短视频App的功能 我们在秒拍官网看到如此描述: [视频拍摄及导入]支持直接拍摄及导入手机本地的视频 [照片电影]照片专属特效,轻松创作照片电影 [MV特效]10余款全新MV特效,让普通视 ...

  4. iOS开发之常用资讯类App的分类展示与编辑的完整案例实现(Swift版)

    上篇博客我们聊了<资讯类App常用分类控件的封装与实现(CollectionView+Swift3.0)>,今天的这篇博客就在上篇博客的基础上做些东西.做一个完整的资讯类App中的分类展示 ...

  5. GNE: 4行代码实现新闻类网站通用爬虫

    GNE(GeneralNewsExtractor)是一个通用新闻网站正文抽取模块,输入一篇新闻网页的 HTML, 输出正文内容.标题.作者.发布时间.正文中的图片地址和正文所在的标签源代码.GNE在提 ...

  6. 三分之一的程序猿之社交类app踩过的那些坑

    三分之一的程序猿之社交类app踩过的那些坑 万众创新,全民创业.哪怕去年陌生人社交不管融资与否都倒闭了不知道多少家,但是依然有很多陌生人社交应用层出不穷的冒出来.各种脑洞大开,让人拍案叫起. 下面我们 ...

  7. 为什么那么多人想开发一元夺宝类app?

    别拿你的无知和愚蠢,来证明主观的判断! 国人对一切事物具有怀疑的本性是好的, 但是若不建立于科学的分析方法, 那就是愚昧! 身边有朋友玩夺宝投入较多,产出较少,于是向我求助.想从数据分析的角度知道到底 ...

  8. 2016年上半年金融类App成绩单,手机银行优势尽显! (转自Analysys易观(ID:enfodesk))

    2016已悄然时过大半,金融各领域经过了开年大战,二季度末尾的6月更是几家欢喜几家愁,其中频繁出现的黑马更是足够让人惊喜.我们基于易观千帆6月移动应用大数据,筛选了百款金融类App为您揭晓TOP100 ...

  9. 移动互联网实战--资源类APP的数据存储处理和优化

    前言: 对于资源类的APP, 其音频/图形占据了APP本身很大的比例. 如何存储和管理这些资源文件, 成了一个颇具挑战性的难点. 移动端的碎片化, 高中低端手机的并存, 需要开发者不光是具备基础的存储 ...

随机推荐

  1. Win10 安装 Linux子系统 Ubuntu18.04 / Kali Linux 的体验

    汇总系列:https://www.cnblogs.com/dunitian/p/4822808.html#linux 几年前就看到新闻,今天周末,突发奇想,家里电脑安装下子系统不就不用安装开发的那些环 ...

  2. list根据某个字段去重

    方法一:使用Set List<User> newList = new ArrayList<User>(); Set<String> set = new HashSe ...

  3. 【CF1141E】Superhero Battle

    \[x*p\ge y\rightarrow x=\lfloor{{y-1}\over p}\rfloor+1\]

  4. request 的介绍使用属性

    上下文:相当于一个容器,保存了 Flask 程序运行过程中的一些信息. Flask中有两种上下文,请求上下文和应用上下文 请求上下文(request context) 在 flask 中,可以直接在视 ...

  5. 第四篇:记录相关操作 SQL逻辑查询语句执行顺序

    http://www.cnblogs.com/linhaifeng/articles/7372774.html 一 SELECT语句关键字的定义顺序 SELECT DISTINCT <selec ...

  6. django2+uwsgi+nginx上线部署到服务器Ubuntu16.04(最新最详细版)

    1.前期准备 1.打开Terminal终端,执行以下命令,将项目所需要的依赖包,都记录到一个文件内备用. pip freeze >requirements.txt 2.将项目文件夹→右键→添加压 ...

  7. SecureCRT或XShell软件

    SecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,简单地说是Windows下登录UNIX或Linux服务器主机的软件. Xshell 是一个强大的安全终端模拟软件,它支持SSH1 ...

  8. Windows Server 2003 添加“Resin”到“服务”出错

    将“Resin”添加到[服务] 进入安装目录,执行 httpd -install 从[服务]移除 执行 httpd -remove ---------------------------------- ...

  9. Unity 着色器基础知识

    一.着色器基础知识 着色器通过代码模拟物体表面发生的事情,其实就是GPU中运行的一段代码. 着色器的类型: 顶点着色器.片元着色器.无光照着色器.表面着色器.图像特效着色器.计算着色器. 坐标空间: ...

  10. idea 红线 并提示idea cant resolve symbol

    能编译通过说明SDK导入正确,但是为啥我们点击每一个Java文件会出现好多红色的下划线 ,并提示idea cant resolve symbol原因就是可能没有清除原来的历史缓存,导致一些错误,解决方 ...