Swift - 分页菜单的实现(使用PagingMenuController库实现tab标签切换)
分页菜单(分段菜单)在许多
App 上都会用到。比如大多数新闻
App,如网易新闻、今日头条等,顶部都有个导航菜单。这个导航菜单是一组标签的集合,每个标签表示一个新闻类别,我们点击这个标签后下面就会切换到相应的分页面。同时左右滑动分页面,上方的标签也会跟着移动。
本文介绍一个优秀的第三方分页视图控件:
PagingMenuController,不仅可以自定义菜单,而且可以自由地修改样式。
一、安装配置
(1)从
GitHub 上下载最新的代码:
https://github.com/kitasuke/PagingMenuController
(2)将下载下来的源码包中
PagingMenuController.xcodeproj 拖拽至你的工程中。
(3)工程 ->
General ->
Embedded Binaries 项,把
PagingMenuController.framework 添加进来。
(4)最后,在需要使用
PagingMenuController 的地方
import 进来就可以了。
1
|
import
PagingMenuController
|
二、纯代码使用样例
1,效果图
(1)主视图顶部分页菜单中有两个菜单标签,分别对应两个子视图。
(2)点击菜单标签,下方便会切换显示相应的子视图。
(3)也可以直接左右滑动子视图进行切换,上方的菜单标签状态也会同步更新。
2,样例代码
(1)子视图控制器1(
ViewController1.swift)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import
UIKit
//子视图控制器1
class
ViewController1
:
UIViewController
{
override
func
viewDidLoad() {
super
.viewDidLoad()
view.backgroundColor =
UIColor
.orange
let
textLabel =
UILabel
(frame:
CGRect
(x: 0, y: 100, width:
self
.view.frame.width,
height: 30))
textLabel.textAlignment = .center
textLabel.font =
UIFont
.systemFont(ofSize: 33)
textLabel.textColor = .white
textLabel.text =
"电影"
view.addSubview(textLabel)
}
}
|
(2)子视图控制器2(
ViewController2.swift)
(3)主视图控制器( ViewController.swift)
源码下载:
hangge_1656.zip
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import
UIKit
//子视图控制器2
class
ViewController2
:
UIViewController
{
override
func
viewDidLoad() {
super
.viewDidLoad()
view.backgroundColor =
UIColor
.darkGray
let
textLabel =
UILabel
(frame:
CGRect
(x: 0, y: 100, width:
self
.view.frame.width,
height: 30))
textLabel.textAlignment = .center
textLabel.font =
UIFont
.systemFont(ofSize: 33)
textLabel.textColor = .white
textLabel.text =
"音乐"
view.addSubview(textLabel)
}
}
|
(3)主视图控制器( ViewController.swift)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
import
UIKit
import
PagingMenuController
//分页菜单配置
private
struct
PagingMenuOptions
:
PagingMenuControllerCustomizable
{
//第1个子视图控制器
private
let
viewController1 =
ViewController1
()
//第2个子视图控制器
private
let
viewController2 =
ViewController2
()
//组件类型
fileprivate
var
componentType:
ComponentType
{
return
.all(menuOptions:
MenuOptions
(), pagingControllers: pagingControllers)
}
//所有子视图控制器
fileprivate
var
pagingControllers: [
UIViewController
] {
return
[viewController1, viewController2]
}
//菜单配置项
fileprivate
struct
MenuOptions
:
MenuViewCustomizable
{
//菜单显示模式
var
displayMode:
MenuDisplayMode
{
return
.segmentedControl
}
//菜单项
var
itemsOptions: [
MenuItemViewCustomizable
] {
return
[
MenuItem1
(),
MenuItem2
()]
}
}
//第1个菜单项
fileprivate
struct
MenuItem1
:
MenuItemViewCustomizable
{
//自定义菜单项名称
var
displayMode:
MenuItemDisplayMode
{
return
.text(title:
MenuItemText
(text:
"电影"
))
}
}
//第2个菜单项
fileprivate
struct
MenuItem2
:
MenuItemViewCustomizable
{
//自定义菜单项名称
var
displayMode:
MenuItemDisplayMode
{
return
.text(title:
MenuItemText
(text:
"音乐"
))
}
}
}
//主视图控制器
class
ViewController
:
UIViewController
{
override
func
viewDidLoad() {
super
.viewDidLoad()
//分页菜单配置
let
options =
PagingMenuOptions
()
//分页菜单控制器初始化
let
pagingMenuController =
PagingMenuController
(options: options)
//分页菜单控制器尺寸设置
pagingMenuController.view.frame.origin.y += 64
pagingMenuController.view.frame.size.height -= 64
//建立父子关系
addChildViewController(pagingMenuController)
//分页菜单控制器视图添加到当前视图中
view.addSubview(pagingMenuController.view)
}
override
func
didReceiveMemoryWarning() {
super
.didReceiveMemoryWarning()
}
}
|
三、Storyboard 使用样例
1,效果图
具体功能同上面的是一样的。
2,Storyboard 相关操作
(1)在主视图中添加一个 Container View,并设置好相关约束。
(2)
Container View 默认embed的是
UIViewController,我们将其改成
PagingMenuController。
3,样例代码
(1)子视图控制器(
ViewController1.swift、
ViewController2.swift)
1
|
//具体参考上面的纯代码实现部分
|
(2)主视图控制器( ViewController.swift)
高亮处表示与上面纯代码实现不相同的地方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
import
UIKit
import
PagingMenuController
//分页菜单配置
private
struct
PagingMenuOptions
:
PagingMenuControllerCustomizable
{
//第1个子视图控制器
private
let
viewController1 =
ViewController1
()
//第2个子视图控制器
private
let
viewController2 =
ViewController2
()
//组件类型
fileprivate
var
componentType:
ComponentType
{
return
.all(menuOptions:
MenuOptions
(), pagingControllers: pagingControllers)
}
//所有子视图控制器
fileprivate
var
pagingControllers: [
UIViewController
] {
return
[viewController1, viewController2]
}
//菜单配置项
fileprivate
struct
MenuOptions
:
MenuViewCustomizable
{
//菜单显示模式
var
displayMode:
MenuDisplayMode
{
return
.segmentedControl
}
//菜单项
var
itemsOptions: [
MenuItemViewCustomizable
] {
return
[
MenuItem1
(),
MenuItem2
()]
}
}
//第1个菜单项
fileprivate
struct
MenuItem1
:
MenuItemViewCustomizable
{
//自定义菜单项名称
var
displayMode:
MenuItemDisplayMode
{
return
.text(title:
MenuItemText
(text:
"电影"
))
}
}
//第2个菜单项
fileprivate
struct
MenuItem2
:
MenuItemViewCustomizable
{
//自定义菜单项名称
var
displayMode:
MenuItemDisplayMode
{
return
.text(title:
MenuItemText
(text:
"音乐"
))
}
}
}
//主视图控制器
class
ViewController
:
UIViewController
{
override
func
viewDidLoad() {
super
.viewDidLoad()
//获取分页菜单配置
let
options =
PagingMenuOptions
()
//设置分页菜单配置
let
pagingMenuController =
self
.childViewControllers.first
as
!
PagingMenuController
pagingMenuController.setup(options)
}
override
func
didReceiveMemoryWarning() {
super
.didReceiveMemoryWarning()
}
}
|
四、标签、页面切换响应
有时我们需要监听页面切换事件进行一些操作,比如当切换到新的页面时可以去请求数据等。这个通过
PagingMenuController 的
onMove 回调就可以实现。每当菜单移动前后、页面切换前后、手指滑动页面前后,该方法都会被调用。
1,样例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
//分页菜单控制器初始化
let
pagingMenuController =
PagingMenuController
(options: options)
//....省略一些代码
view.addSubview(pagingMenuController.view)
//页面切换响应
pagingMenuController.onMove = { state
in
switch
state {
case
let
.willMoveItem(menuItemView, previousMenuItemView):
print
(
"--- 标签将要切换 ---"
)
print
(
"老标签:\(previousMenuItemView.titleLabel.text!)"
)
print
(
"新标签:\(menuItemView.titleLabel.text!)"
)
case
let
.didMoveItem(menuItemView, previousMenuItemView):
print
(
"--- 标签切换完毕 ---"
)
print
(
"老标签:\(previousMenuItemView.titleLabel.text!)"
)
print
(
"新标签:\(menuItemView.titleLabel.text!)"
)
case
let
.willMoveController(menuController, previousMenuController):
print
(
"--- 页面将要切换 ---"
)
print
(
"老页面:\(previousMenuController)"
)
print
(
"新页面:\(menuController)"
)
case
let
.didMoveController(menuController, previousMenuController):
print
(
"--- 页面切换完毕 ---"
)
print
(
"老页面:\(previousMenuController)"
)
print
(
"新页面:\(menuController)"
)
case
.didScrollStart:
print
(
"--- 分页开始左右滑动 ---"
)
case
.didScrollEnd:
print
(
"--- 分页停止左右滑动 ---"
)
}
}
|
2,运行效果
(1)我们点击顶部菜单标签进行页面切换,可以看到控制台打印出如下信息:(2)而如果通过手指滑动切换页面,控制台打印出如下信息:
五、使用代码切换标签
比如下面代码自动将分页菜单控制器切换到第
2 个页面。
1
|
pagingMenuController.move(toPage: 1, animated:
true
)
|
六、自定义分页控制器样式
PagingMenuControllerCustomizable 对象有如下几个属性可以对整个视图控制器进行自定义:
1,defaultPage: Int
设置默认页面的索引,如果不指定则默认显示第一个视图页。
2,animationDuration: TimeInterval
页面切换动画时间。
3,isScrollEnabled: Bool
是否允许手指左右滑动进行页面切换,设置为
false 则只能点击菜单标签切换页面。
4,backgroundColor: UIColor
设置页面背景色。
5,lazyLoadingPage: LazyLoadingPage
lazy loading 的页面数量,这个是一个枚举,可选值如下:
1
2
3
4
5
|
public
enum
LazyLoadingPage
{
case
one
// Currently sets false to isScrollEnabled at this moment. Should be fixed in the future.
case
three
case
all
// Currently not available for Infinite mode
}
|
6,menuControllerSet: MenuControllerSet
这个不太清楚做什么用的,也是一个枚举可选值如下:
1
2
3
4
|
public
enum
MenuControllerSet
{
case
single
case
multiple
}
|
7,componentType: ComponentType
这个很重要,用来配置整个分页菜单控制器包含的页面和菜单标签。这也是一个枚举,可选值如下:
其中的 MenuItemWidthMode、 MenuScrollingMode 也是枚举:
1
2
3
4
5
|
public
enum
ComponentType
{
case
menuView(menuOptions:
MenuViewCustomizable
)
case
pagingController(pagingControllers: [
UIViewController
])
case
all(menuOptions:
MenuViewCustomizable
, pagingControllers: [
UIViewController
])
}
|
8,下面是一个完整的配置样例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
//分页菜单配置
private
struct
PagingMenuOptions
:
PagingMenuControllerCustomizable
{
//默认显示第2页
var
defaultPage:
Int
= 1
//页面切换动画播放时间为0.5秒
var
animationDuration:
TimeInterval
= 0.5
//不允许手指左右滑动页面切换
var
isScrollEnabled:
Bool
=
false
//页面背景色为紫色
var
backgroundColor:
UIColor
= .purple
//lazy loading的页面数量(默认值就是.three)
var
lazyLoadingPage:
LazyLoadingPage
= .three
//不太清楚干嘛用的(默认值就是.multiple)
var
menuControllerSet:
MenuControllerSet
= .multiple
//第1个子视图控制器
private
let
viewController1 =
ViewController1
()
//第2个子视图控制器
private
let
viewController2 =
ViewController2
()
//组件类型
fileprivate
var
componentType:
ComponentType
{
return
.all(menuOptions:
MenuOptions
(), pagingControllers: pagingControllers)
}
//所有子视图控制器
fileprivate
var
pagingControllers: [
UIViewController
] {
return
[viewController1, viewController2]
}
//菜单配置项
fileprivate
struct
MenuOptions
:
MenuViewCustomizable
{
//菜单显示模式
var
displayMode:
MenuDisplayMode
{
return
.segmentedControl
}
//菜单项
var
itemsOptions: [
MenuItemViewCustomizable
] {
return
[
MenuItem1
(),
MenuItem2
()]
}
}
//第1个菜单项
fileprivate
struct
MenuItem1
:
MenuItemViewCustomizable
{
//自定义菜单项名称
var
displayMode:
MenuItemDisplayMode
{
return
.text(title:
MenuItemText
(text:
"电影"
))
}
}
//第2个菜单项
fileprivate
struct
MenuItem2
:
MenuItemViewCustomizable
{
//自定义菜单项名称
var
displayMode:
MenuItemDisplayMode
{
return
.text(title:
MenuItemText
(text:
"音乐"
))
}
}
}
|
七、自定义菜单栏样式
MenuViewCustomizable 对象有如下几个属性对菜单栏进行自定义:
1,backgroundColor: UIColor
设置未选中的菜单标签背景色。
2,selectedBackgroundColor: UIColor
设置选中的菜单标签背景色。
3,height: CGFloat
设置菜单标签的高度。
4,animationDuration: TimeInterval
设置菜单标签切换时动画持续时长。
5,deceleratingRate: CGFloat
设置菜单切换动画减速率(默认为:
UIScrollViewDecelerationRateFast)
6,menuSelectedItemCenter: Bool
不太清楚干嘛的。
7,displayMode: MenuDisplayMode
菜单显示模式,它是一个枚举有三个可选值:
1
2
3
4
5
6
7
|
public
enum
MenuDisplayMode
{
case
standard(widthMode:
MenuItemWidthMode
, centerItem:
Bool
,
scrollingMode:
MenuScrollingMode
)
case
segmentedControl
case
infinite(widthMode:
MenuItemWidthMode
,
scrollingMode:
MenuScrollingMode
)
//这个至少要求有3个页面
}
|
其中的 MenuItemWidthMode、 MenuScrollingMode 也是枚举:
1
2
3
4
5
6
7
8
9
10
11
12
|
//设置菜单标签的宽度
public
enum
MenuItemWidthMode
{
case
flexible
//自动
case
fixed(width:
CGFloat
)
//固定宽度
}
//菜单如何滚动
public
enum
MenuScrollingMode
{
case
scrollEnabled
//可以跨多个菜单项切换
case
scrollEnabledAndBouces
//可以跨多个菜单项切换
case
pagingEnabled
//菜单只能一个接一个切换
}
|
- 样例1:分段模式
1
2
3
4
|
//菜单显示模式(分段模式)
var
displayMode:
MenuDisplayMode
{
return
.segmentedControl
}
|
- 样例2:标准模式,这里我让选中标签始终居中
1
2
3
4
5
|
//菜单显示模式(标准模式)
var
displayMode:
MenuDisplayMode
{
return
.standard(widthMode: .fixed(width: 30), centerItem:
true
,
scrollingMode: .pagingEnabled)
}
|
- 样例3:无限循环模式(这个必需至少有三个菜单项)
1
2
3
4
|
//菜单显示模式(无限循环模式)
var
displayMode:
MenuDisplayMode
{
return
.infinite(widthMode: .flexible, scrollingMode: .pagingEnabled)
}
|
8,focusMode: MenuFocusMode
选中菜单标签的样式,这个也是枚举:
1
2
3
4
5
6
7
|
public
enum
MenuFocusMode
{
case
none
case
underline(height:
CGFloat
, color:
UIColor
, horizontalPadding:
CGFloat
,
verticalPadding:
CGFloat
)
case
roundRect(radius:
CGFloat
, horizontalPadding:
CGFloat
, verticalPadding:
CGFloat
,
selectedColor:
UIColor
)
}
|
- 样例1:无样式
1
2
|
//选中项无样式
var
focusMode:
MenuFocusMode
= .none
|
- 样例2:下划线样式
1
2
3
|
//选中项为橙色下划线样式
var
focusMode:
MenuFocusMode
= .underline(height: 3, color: .orange, horizontalPadding: 0,
verticalPadding: 0)
|
- 样例3:圆角矩形背景样式
1
2
3
|
//选中项为橙色矩形背景
var
focusMode:
MenuFocusMode
= .roundRect(radius: 6, horizontalPadding: 5,
verticalPadding: 8, selectedColor: .orange)
|
9,dummyItemViewsSet: Int
不太清楚干嘛用的。10,menuPosition: MenuPosition
菜单栏的位置,我们可以指定菜单栏在页面视图的上方还是下方。
这个也是个枚举:
1
2
3
4
|
public
enum
MenuPosition
{
case
top
case
bottom
}
|
11,dividerImage: UIImage?
菜单标签间的分隔图片,显示在每个标签的右侧。
1
2
|
//设置标签间的分隔图片
var
dividerImage:
UIImage
? =
UIImage
(named:
"dividerImage.png"
)!
|
12,下面是完整的使用样例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
//菜单栏配置
fileprivate
struct
MenuOptions
:
MenuViewCustomizable
{
//设置未选中的菜单标签背景为深灰色
var
backgroundColor:
UIColor
= .darkGray
//设置选中的菜单标签背景为浅浅灰色
var
selectedBackgroundColor:
UIColor
= .lightText
//设置菜单标签高度为40
var
height:
CGFloat
= 40
//菜单切换动画播放时间为0.5秒
var
animationDuration:
TimeInterval
= 0.5
//菜单切换动画减速率(默认为:UIScrollViewDecelerationRateFast)
var
deceleratingRate:
CGFloat
=
UIScrollViewDecelerationRateFast
//不知道干嘛的
var
menuSelectedItemCenter:
Bool
=
false
//菜单显示模式(分段模式)
var
displayMode:
MenuDisplayMode
{
return
.segmentedControl
}
//选中项为橙色矩形背景
var
focusMode:
MenuFocusMode
= .roundRect(radius: 6, horizontalPadding: 5,
verticalPadding: 8, selectedColor: .orange)
//不知道干嘛的
var
dummyItemViewsSet:
Int
= 3
//设置菜单栏在下方
var
menuPosition:
MenuPosition
= .bottom
//设置标签间的分隔图片
var
dividerImage:
UIImage
? =
UIImage
(named:
"dividerImage.png"
)!
//菜单项
var
itemsOptions: [
MenuItemViewCustomizable
] {
return
[
MenuItem1
(),
MenuItem2
(),
MenuItem3
() ,
MenuItem4
()]
}
}
|
八、自定义单独的菜单标签样式
MenuItemViewCustomizable 对象有如下几个属性可以对单独的菜单项进行自定义:1,horizontalMargin: CGFloat
设置该菜单标签的左右水平间距。
1
2
3
4
5
6
7
8
|
//第2个菜单项
fileprivate
struct
MenuItem2
:
MenuItemViewCustomizable
{
//该标签的水平边距设为50
var
horizontalMargin:
CGFloat
= 50
//自定义菜单项名称
var
displayMode:
MenuItemDisplayMode
= .text(title:
MenuItemText
(text:
"音乐e"
))
}
|
2,displayMode: MenuItemDisplayMode
设置该菜单标签的显示模式,它是一个枚举:
1
2
3
4
5
6
|
public
enum
MenuItemDisplayMode
{
case
text(title:
MenuItemText
)
//普通标题文本
case
multilineText(title:
MenuItemText
, description:
MenuItemText
)
//标题+描述文本
case
image(image:
UIImage
, selectedImage:
UIImage
?)
//图片
case
custom(view:
UIView
)
//自定义视图
}
|
- 样例1:修改标签文字的颜色和字体(包括选中和未选中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//第1个菜单项
fileprivate
struct
MenuItem1
:
MenuItemViewCustomizable
{
//自定义菜单项名称
var
displayMode:
MenuItemDisplayMode
{
return
.text(title:
MenuItemText
(text:
"电影"
,
color: .lightGray, selectedColor: .orange,
font:
UIFont
.systemFont(ofSize: 13) ,
selectedFont:
UIFont
.systemFont(ofSize: 18)))
}
}
//第2个菜单项
fileprivate
struct
MenuItem2
:
MenuItemViewCustomizable
{
//自定义菜单项名称
var
displayMode:
MenuItemDisplayMode
{
return
.text(title:
MenuItemText
(text:
"音乐"
,
color: .lightGray, selectedColor: .orange,
font:
UIFont
.systemFont(ofSize: 13) ,
selectedFont:
UIFont
.systemFont(ofSize: 18)))
}
}
|
- 样例2:标签显示为标题+描述文本
1
2
3
4
5
6
7
8
9
10
11
12
|
//第2个菜单项
fileprivate
struct
MenuItem2
:
MenuItemViewCustomizable
{
//该标签的水平边距设为50
var
horizontalMargin:
CGFloat
= 50
//自定义菜单项标题和描述
var
displayMode:
MenuItemDisplayMode
{
let
desFont =
UIFont
.systemFont(ofSize: 10)
let
description =
MenuItemText
(text:
"Music"
, font: desFont , selectedFont: desFont )
return
.multilineText(title:
MenuItemText
(text:
"音乐"
), description: description)
}
}
|
- 样例3:将菜单标签显示为图片
1
2
3
4
5
6
7
8
9
10
11
|
//第2个菜单项
fileprivate
struct
MenuItem2
:
MenuItemViewCustomizable
{
//该标签的水平边距设为50
var
horizontalMargin:
CGFloat
= 50
//菜单项显示为图片
var
displayMode:
MenuItemDisplayMode
{
return
.image(image:
UIImage
(named:
"forward"
)!,
selectedImage:
UIImage
(named:
"forward"
)!)
}
}
|
转载自链接: http://www.hangge.com/blog/cache/detail_1656.html