突然感觉,上班也跟上学的时候很像,有业务精的,也有懒散的,有聪明的,有踏实肯干的。不同的也许是,不再靠一张试卷决定成绩,而是夹杂了很多人情在里面。作为程序员,写代码就像写作业,多思考,多练习,才能有所助益。对于我这种资质愚笨的“学生”而言,就更应该勤加练习。最近写了一个简单的滑动切换菜单的小组件,目前只支持最简单的平分屏幕,底部下划线的样式。这里记录一下实现思路以及思考过程,一是留作日后翻看,二是拿出来大家讨论,一起进步。
github地址:
https://github.com/weiman152/ScrollMenusView
先看实现效果:
![](https://img.haomeiwen.com/i1293851/030d97954e895e6e.gif)
一、实现思路
我这里希望这个组件作为一个View进行使用,内部使用的也都是View实现的。
-
结构
我把这个组件分成了三部分,第一部分是顶部的按钮部分。第二部分是中间的滑动小细线。第三部分是下面的内容部分。
image.png
menus: 顶部按钮
line:中间细线
collectionView:底部内容部分
为什么这样分?
我是根据这个组件各自不同的功能进行封装拆分的。
顶部按钮负责切换菜单,它是独立的部分,至于内容如何排列,如何实现切换,是顶部按钮自己的事,可以封装在一个View中。中间的小横线只负责滑动,滑动到哪里它自己不知道,需外界告知,但是自己的大小颜色需要自己管理。底部的内容负责响应左右扫的手势,告知外界自己滑动了多少。
-
顶部按钮
我的顶部实现就是用了View + button。传进来按钮的名字以及图片,然后使用for循环创建按钮,依次排列。按钮的大小是根据屏幕变化,平分整个屏幕宽度。
部分代码:
image.png
3.中间细线
整个细线就是一个View,设置了颜色和大小,没有什么特殊操作,并没有单独拿出来。
4.底部内容
底部内容考虑到要滑动,就使用了UICollectionView。每一个cell对应一个顶部的菜单。
5.如何同步
这个问题应该是实现这个组件的重点了吧。
首先是按钮与细线和内容的同步。
在创建按钮的时候,记录按钮的index,我这里使用的是button的属性tag。点击按钮的时候,如果点击的按钮tag与当前正在显示的按钮不是同一个,则发出按钮切换的信号。使用代理,告知上层View,按钮切换了。
![](https://img.haomeiwen.com/i1293851/2f1a4078af4c9ce0.png)
上层View收到这个消息之后,把细线和内容切换到对应的位置。当然了,底部collectionView是通过contentOffset进行滑动的。
![](https://img.haomeiwen.com/i1293851/57f531318bf27a18.png)
其次是底部滑动内容与顶部的按钮和细线的同步。
底部的滑动是通过监听collectionView的滑动代理,确定滑动的位置,再通过位置计算出index,把index通过代理传给上层,最后实现同步。
![](https://img.haomeiwen.com/i1293851/e4fb6978127c39c5.png)
这里解释下,为什么使用scrollViewDidEndDecelerating而不是scrollViewDidScroll。因为scrollViewDidEndDecelerating是滑动停止的时候调用的,而scrollViewDidScroll只要滑动都会调用,不停地计算会消耗性能,我们关心的也只是滑动最后停止的位置而不是过程,所以我这里选择使用scrollViewDidEndDecelerating来确定最后停止的index。上层View收到消息后,设置顶部按钮以及细线的位置。
![](https://img.haomeiwen.com/i1293851/884b9d3cf51fca55.png)
二、难点
说是难点,其实算不上难点,只是计算稍微花点时间,用到的知识也只是小学的等比运算而已。
1.细线的滑动
这里的细线是根据底部内容滑动的多少来同步滑动的。细线的滑动其实就是细线的X在不断地变化。向右滑动,X变大;向左滑动,X变小。
示意图(凑合看吧)
![](https://img.haomeiwen.com/i1293851/bf7c935dd2c2cf54.jpg)
所以,我们就可以这么计算了:
![](https://img.haomeiwen.com/i1293851/128d4cd3b31ec3f5.png)
![](https://img.haomeiwen.com/i1293851/ece374d38b56d211.png)
2.暴露给外部的接口设计
这个组件的作用就是实现菜单的滑动,菜单内容和底部内容部分是用户自己要写的部分。我希望这个组件像tableview那样使用,当然了,自己写的与tableView差了一个银河系呢。
![](https://img.haomeiwen.com/i1293851/e78bf0fc75512a11.png)
初始化的时候,传入顶部菜单的内容。支持纯文字和文字+图片的样式。
![](https://img.haomeiwen.com/i1293851/b904687b03ceaa8e.png)
三、缺点
设计上也许不够完善,期待高人指点。
四、使用:
private func setup() {
let menu1 = MenuModel(title: "购买记录",
imageNormal: nil,
imageSelected: nil)
let menu2 = MenuModel(title: "680人",
imageNormal: #imageLiteral(resourceName: "personNumber_normal"),
imageSelected: #imageLiteral(resourceName: "personNumber_selected"))
let menu3 = MenuModel(title: "商品详情",
imageNormal: nil,
imageSelected: nil)
menus = [menu1, menu2, menu3]
let menu = ScrollMenus(titles: menus,
frame: menuView.bounds,
menuHeight: 44)
menu.dataSource = self
menu.delegate = self
menu.set(selected: 1)
menu.lineColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
menu.textColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
menu.textSeletedColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
menuView.addSubview(menu)
scrollMenu = menu
}
extension ViewController: ScrollMenusDataSource {
func menuViewNumberOfItems() -> Int {
return menus.count
}
func menuViewViewForItems(atIndex: Int) -> UIView {
guard atIndex < menus.count, atIndex < childs.count else {
return UIView()
}
return childs[atIndex].view
}
}
extension ViewController: ScrollMenusDelegate {
func menuDidChange(currentIndex: Int) {
print("------------ \(currentIndex)")
}
}
五、集成
-
直接导入项目中,导入方法就不多啰嗦了。
image.png
2.使用cocoa pods继承
platform :ios, '9.0' inhibit_all_warnings!
target '你的项目名字' do use_frameworks!
pod 'ScrollMenusView'
end
最后,希望自己继续静心学习,希望大家天天开心,更期待给出批评和建议。
网友评论