PagingKit为自定义菜单和内容UI提供。它比其他库有更灵活的布局和设计。
这是什么?
有许多库提供“分页UI”,它们都有菜单和内容区域。它们很方便,但不可自定义,因为您的应用程序必须与库的布局和视图组件兼容。当您的应用程序的UI设计不适合库时,您需要分叉它们或寻找另一个。
PagingKit比其他库有更灵活的布局和设计。您可以构建“菜单”和“内容”UI,并且它们可以一起使用。这就是这个库提供的所有功能。您可以按您喜欢的任何设计将任何设计适合到您的应用程序。
定制布局
您可以按您喜欢的方式对齐视图。
更改位置 | 将视图放置在内容和菜单之间 | 在右侧添加浮动按钮 | 在导航栏上 |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
定制菜单设计
您可以按您喜欢的任何方式自定义菜单。
标签样式菜单 | 高亮文本菜单 | 下划线菜单 | App Store应用样式指示器 |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
我在寻找您自定义菜单设计的拉取请求 :)
特性
- 易于构建分页UI
- 可定制布局和设计
- 类似UIKit的API
- 支持iPhone、iPad和iPhone X
需求
- iOS 9.0+
- Xcode 11.0+
- Swift 5.1
安装
Swift 包管理器
打开 Swift 包文件(位于构建设置旁边)。您可以从此标签页添加和删除包。
CocoaPods
- 安装 CocoaPods
> gem install cocoapods
> pod setup
- 创建 Podfile
> pod init
- 编辑 Podfile
target 'YourProject' do
use_frameworks!
pod "PagingKit" # add
target 'YourProject' do
inherit! :search_paths
end
target 'YourProject' do
inherit! :search_paths
end
end
- 安装
> pod install
打开 .xcworkspace
Cartfile
- 使用 Homebrew 安装 Carthage
> ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
> brew update
> brew install carthage
- 移动您的项目目录并创建 Cartfile
> touch Cartfile
- 在 Cartfile 中添加以下行
github "kazuhiro4949/PagingKit"
- 创建框架
> carthage update --platform iOS
- 在Xcode中,转到“生成 > 生成过程 > 链接框架和库”
- 将框架添加到您的项目中
- 添加一个新的运行脚本并将以下代码放入其中
/usr/local/bin/carthage copy-frameworks
- 在“输入文件”处单击"+"并添加框架路径
$(SRCROOT)/Carthage/Build/iOS/PagingKit.framework
- 在源文件上写入导入语句
import PagingKit
入门指南
本库中包含一些示例。
https://github.com/kazuhiro4949/PagingKit/tree/master/iOS%20Sample/iOS%20Sample
您可以将PagingKit按示例那样集成到您的项目中。查看此存储库并打开工作区。
PagingKit有两个核心类。
- PagingMenuViewController
- PagingContentViewController
PagingMenuViewController为每个内容提供交互式菜单。PagingContentViewController在滚动视图中提供内容。
如果您创建了一个包含PagingKit的新项目,请按照以下步骤操作。
- 添加PagingMenuViewController和PagingContentViewController
- 将它们分配到属性
- 创建菜单UI
- 显示数据
- 同步菜单和内容视图控制器
1. 添加PagingMenuViewController和PagingContentViewController
首先,请在Storyboard中的容器视图中添加PagingMenuViewController和PagingContentViewController。
1. 将容器视图放在Storyboard中
为每个视图控制器将容器视图放在Storyboard中。
2. 更改类名
在自定义类设置中输入PagingMenuViewController。
在自定义类设置中输入PagingContentViewController。
2. 将它们分配给属性
在容器的视图控制器代码中分配它们。
1. 为视图控制器声明属性
在容器视图控制器中声明属性。
class ViewController: UIViewController {
var menuViewController: PagingMenuViewController!
var contentViewController: PagingContentViewController!
2. 覆盖 prepare(segue:sender:) 并分配视图控制器
将视图控制器分配给 prepare(segue:sender:)
中的每个属性。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? PagingMenuViewController {
menuViewController = vc
} else if let vc = segue.destination as? PagingContentViewController {
contentViewController = vc
}
}
3. 构建应用
构建并检查当前状态。
显示了包含 PagingMenuViewController 和 PagingContentViewController 的容器视图控制器。
3. 创建菜单 UI
接下来,你需要准备菜单元素。
1. 继承PagingMenuViewCell并创建自定义单元格
PagingKit包含PagingMenuViewCell。PagingMenuViewController使用它来构建每个菜单元素。
import UIKit
import PagingKit
class MenuCell: PagingMenuViewCell {
@IBOutlet weak var titleLabel: UILabel!
}
2. 继承PagingFocusView并创建自定义视图
PagingKit有PagingFocusView。PagingMenuViewController使用它来指示当前的焦点菜单。
3. 将上述视图注册到PagingMenuViewController
class ViewController: UIViewController {
var menuViewController: PagingMenuViewController!
var contentViewController: PagingContentViewController!
override func viewDidLoad() {
super.viewDidLoad()
menuViewController.register(nib: UINib(nibName: "MenuCell", bundle: nil), forCellWithReuseIdentifier: "MenuCell")
menuViewController.registerFocusView(nib: UINib(nibName: "FocusView", bundle: nil))
}
4. 显示数据
然后,实现数据源来显示内容。它们类似于UITableViewDataSource。
1. 准备数据
class ViewController: UIViewController {
static var viewController: (UIColor) -> UIViewController = { (color) in
let vc = UIViewController()
vc.view.backgroundColor = color
return vc
}
var dataSource = [(menuTitle: "test1", vc: viewController(.red)), (menuTitle: "test2", vc: viewController(.blue)), (menuTitle: "test3", vc: viewController(.yellow))]
2. 设置菜单数据源
返回菜单数量、菜单宽度和PagingMenuViewCell对象。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? PagingMenuViewController {
menuViewController = vc
menuViewController.dataSource = self // <- set menu data source
} else if let vc = segue.destination as? PagingContentViewController {
contentViewController = vc
}
}
}
extension ViewController: PagingMenuViewControllerDataSource {
func numberOfItemsForMenuViewController(viewController: PagingMenuViewController) -> Int {
return dataSource.count
}
func menuViewController(viewController: PagingMenuViewController, widthForItemAt index: Int) -> CGFloat {
return 100
}
func menuViewController(viewController: PagingMenuViewController, cellForItemAt index: Int) -> PagingMenuViewCell {
let cell = viewController.dequeueReusableCell(withReuseIdentifier: "MenuCell", for: index) as! MenuCell
cell.titleLabel.text = dataSource[index].menuTitle
return cell
}
}
3. 配置内容数据源
返回内容和视图控制器数量。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? PagingMenuViewController {
menuViewController = vc
menuViewController.dataSource = self
} else if let vc = segue.destination as? PagingContentViewController {
contentViewController = vc
contentViewController.dataSource = self // <- set content data source
}
}
}
extension ViewController: PagingContentViewControllerDataSource {
func numberOfItemsForContentViewController(viewController: PagingContentViewController) -> Int {
return dataSource.count
}
func contentViewController(viewController: PagingContentViewController, viewControllerAt index: Int) -> UIViewController {
return dataSource[index].vc
}
}
4. 加载UI
从起始点开始调用reloadData()方法。
override func viewDidLoad() {
super.viewDidLoad()
//...
//...
menuViewController.reloadData()
contentViewController.reloadData()
}
构建和显示数据源。
5. 同步菜单和内容视图控制器
最后,同步菜单与内容之间的用户交互。
1. 设置菜单代理
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? PagingMenuViewController {
menuViewController = vc
menuViewController.dataSource = self
menuViewController.delegate = self // <- set menu delegate
} else if let vc = segue.destination as? PagingContentViewController {
contentViewController = vc
contentViewController.dataSource = self
}
}
}
实现菜单代理来处理事件。它与UITableViewDelegate相似。您需要在不率分页内容视图控制器的代理方法中实现滚动方法。
extension ViewController: PagingMenuViewControllerDelegate {
func menuViewController(viewController: PagingMenuViewController, didSelect page: Int, previousPage: Int) {
contentViewController.scroll(to: page, animated: true)
}
}
2. 设置内容代理
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? PagingMenuViewController {
menuViewController = vc
menuViewController.dataSource = self
menuViewController.delegate = self
} else if let vc = segue.destination as? PagingContentViewController {
contentViewController = vc
contentViewController.dataSource = self
contentViewController.delegate = self // <- set content delegate
}
}
}
实现内容代理来处理事件。它与UIScrollViewDelegate类似。您需要实现分页菜单视图控制器的滚动事件。其中,“percent”是从“index”参数到右侧页面索引的距离。
extension ViewController: PagingContentViewControllerDelegate {
func contentViewController(viewController: PagingContentViewController, didManualScrollOn index: Int, percent: CGFloat) {
menuViewController.scroll(index: index, percent: percent, animated: false)
}
}
就是这样。
提示
- 内置UI组件
- 聚焦单元格样式
- 单元格对齐
- 下划线与每个标题标签保持相同的宽度
- 控制PagingContentViewController的滚动
- 无Storyboard初始化
- 将菜单放在UINavigationBar上
- 与PagingMenuFocusView一起动画化
- 支持从右到左(RTL)
- 代码片段
类设计
在这个库中存在一些设计策略。
- 行为需要由库指定。
- 布局应留给开发人员。
- 内部组件的排列必须留给开发人员。
正因为如此,PagingKit负责行为。但它不指定组件的结构。PagingKit更喜欢组合而不是继承。此图描述了类图的概览。
与RxSwift一起工作
PagingKit与RxSwift配合良好。
https://github.com/kazuhiro4949/RxPagingKit
let items = PublishSubject<[(menu: String, width: CGFloat, content: UIViewController)]>()
override func viewDidLoad() {
super.viewDidLoad()
menuViewController?.register(type: TitleLabelMenuViewCell.self, forCellWithReuseIdentifier: "identifier")
menuViewController?.registerFocusView(view: UnderlineFocusView())
// PagingMenuViewControllerDataSource
items.asObserver()
.map { items in items.map({ ($0.menu, $0.width) }) }
.bind(to: menuViewController.rx.items(
cellIdentifier: "identifier",
cellType: TitleLabelMenuViewCell.self)
) { _, model, cell in
cell.titleLabel.text = model
}
.disposed(by: disposeBug)
// PagingContentViewControllerDataSource
items.asObserver()
.map { items in items.map({ $0.content }) }
.bind(to: contentViewController.rx.viewControllers())
.disposed(by: disposeBug)
// PagingMenuViewControllerDelegate
menuViewController.rx.itemSelected.asObservable().subscribe(onNext: { [weak self] (page, prev) in
self?.contentViewController.scroll(to: page, animated: true)
}).disposed(by: disposeBug)
// PagingContentViewControllerDelegate
contentViewController.rx.didManualScroll.asObservable().subscribe(onNext: { [weak self] (index, percent) in
self?.menuViewController.scroll(index: index, percent: percent, animated: false)
}).disposed(by: disposeBug)
}
许可证
版权所有(c)2017 Kazuhiro Hayashi
任何人如获得本软件及其相关文档文件(以下简称“软件”)的副本,均有权无偿处理该软件,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件的副本,并允许向软件提供副本的人以同样的方式做,只要遵守以下条件
上述版权声明和本许可协议应包含在软件的所有副本或主要部分中。
本软件按“原样”提供,不提供任何明示或暗示的保证,包括但不限于对适销性、针对特定目的的适用性和非侵权性的保证。在任何情况下,作者或版权持有者均不对任何索赔、损害或其他责任负责,无论这种责任是基于合同、侵权或其他法律依据,无论这种责任产生于、源于或与本软件或其使用或其他交易有关。