NestedPageViewController
iOS嵌套联动分页控制器
Install / Use
/learn @SPStore/NestedPageViewControllerREADME
<p align="center">
<img src="https://raw.githubusercontent.com/SPStore/NestedPageViewController/master/Assets/NestedPageViewController_logo.png" title="NestedPageViewController logo" width="480">
</p>
<p align="center">
一个用于iOS的嵌套页面视图控制器,提供平滑的滚动协调体验。
</p>
<p align="center">
<a href="https://github.com/SPStore/NestedPageViewController"><img src="https://img.shields.io/badge/platform-iOS-blue.svg"></a>
<a href="https://swift.org/"><img src="https://img.shields.io/badge/Swift-5.0-orange.svg"></a>
<a href="https://developer.apple.com/ios/"><img src="https://img.shields.io/badge/iOS-13.0%2B-blue.svg"></a>
<a href="https://github.com/SPStore/NestedPageViewController/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg"></a>
<a href="https://cocoapods.org/pods/NestedPageViewController"><img src="https://img.shields.io/badge/pod-v2.0.0-brightgreen.svg"></a>
<a href="https://swift.org/package-manager/"><img src="https://img.shields.io/badge/SPM-compatible-brightgreen.svg"></a>
</p>
功能特点
- [x] 支持头部视图、标签栏和多个子视图控制器
- [x] 支持内容滚动位置记录(该功能是设计本框架的最大动力来源)
- [x] 支持局部刷新和全局刷新
- [x] 支持子页面预加载(默认是滑动到指定页才加载)
- [x] 支持头部视图手指拖拽滚动并带动整体,可配置控制内容scrollView是否惯性滚动
- [x] 支持自定义标签栏
- [x] 支持旋转
- [x] 更多细节和功能请下载demo
功能演示
<table> <tr bgcolor="#f2f2f2"> <td width="250" align="center"><strong>记录滚动位置</strong></td> <td width="250" align="center"><strong>局部刷新</strong></td> <td width="250" align="center"><strong>全局刷新</strong></td> </tr> <tr> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/记录滚动位置.gif" width="250" alt="记录滚动位置"></td> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/局部刷新.gif" width="250" alt="局部刷新"></td> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/全局刷新.gif" width="250" alt="全局刷新"></td> </tr> <tr bgcolor="#f2f2f2"> <td align="center"><strong>头部始终固定不动</strong></td> <td align="center"><strong>头部缩放+导航栏隐藏</strong></td> <td align="center"><strong>显示底部tabBar</strong></td> </tr> <tr> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/头部始终固定不动.gif" width="250" alt="头部始终固定不动"></td> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/头部缩放+隐藏导航栏.gif" width="250" alt="头部缩放+隐藏导航栏"></td> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/显示系统tabBar.gif" width="250" alt="显示底部tabBar"></td> </tr> </tr> <tr bgcolor="#f2f2f2"> <td align="center"><strong>子VC的sectionHeader吸顶</strong></td> <td align="center"><strong>运行时修改头部高度</strong></td> <td align="center"><strong>没有头部</strong></td> </tr> <tr> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/子VC的sectionHeader吸顶.gif" width="250" alt="子VC的sectionHeader吸顶"></td> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/运行时修改头部高度.gif" width="250" alt="运行时修改头部高度"></td> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/没有头部.gif" width="250" alt="没有头部"></td> </tr> <tr bgcolor="#f2f2f2"> <td align="center"><strong>滚到顶部</strong></td> <td align="center"><strong>自定义标签栏1</strong></td> <td align="center"><strong>自定义标签栏2</strong></td> </tr> <tr> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/滚到顶部.gif" width="250" alt="滚到顶部"></td> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/自定义标签栏1.gif" width="250" alt="自定义标签栏1"></td> <td><img src="https://github.com/SPStore/SPExmapleResurces/blob/main/NestedPageViewController/自定义标签栏2.gif" width="250" alt="自定义标签栏2"></td> </table>系统要求
- iOS 13.0+
- Swift 5.0+
安装
Swift Package Manager
在Xcode中,选择 File > Swift Packages > Add Package Dependency,然后输入以下URL:
https://github.com/SPStore/NestedPageViewController.git
CocoaPods
在你的Podfile中添加:
pod 'NestedPageViewController'
然后运行:
pod install
注意:如果CocoaPods的方式安装,编译报错:Xcode error when building app: line 7: /resources-to-copy-Project.txt: Permission denied,或者其他类似权限问题,请在你的主工程中的Targets -> Build Settings -> User Script Sandboxing 改为No
使用方法
NestedPageViewController提供两种使用方式:添加子控制器方式和继承方式。
方式一:添加子控制器方式
import UIKit
import NestedPageViewController
class YourViewController: UIViewController {
// MARK: - Properties
private var nestedPageViewController = NestedPageViewController()
private var coverView = YourHeaderView()
private var customTabStrip = YourCustomTabStrip()
// MARK: - View Controllers
private let childControllerTitles = ["标签一", "标签二", "标签三", "标签四"]
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupNestedPageViewController()
}
// MARK: - Setup
private func setupNestedPageViewController() {
nestedPageViewController.dataSource = self
nestedPageViewController.delegate = self
// 添加为子控制器
addChild(nestedPageViewController)
view.addSubview(nestedPageViewController.view)
nestedPageViewController.didMove(toParent: self)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// 更新NestedPageViewController的frame
let safeAreaTop = view.safeAreaInsets.top
nestedPageViewController.view.frame = CGRect(
x: 0,
y: safeAreaTop,
width: view.bounds.width,
height: view.bounds.height - safeAreaTop
)
}
}
// MARK: - NestedPageViewControllerDataSource
extension YourViewController: NestedPageViewControllerDataSource {
func numberOfViewControllers(in pageViewController: NestedPageViewController) -> Int {
return childControllerTitles.count
}
func pageViewController(_ pageViewController: NestedPageViewController, viewControllerAt index: Int) -> NestedPageScrollable? {
guard index >= 0 && index < childControllerTitles.count else { return nil }
switch index {
case 0:
return YourChildViewController1() // 必须遵守NestedPageScrollable协议
case 1:
return YourChildViewController2() // 必须遵守NestedPageScrollable协议
case 2:
return YourChildViewController3() // 必须遵守NestedPageScrollable协议
case 3:
return YourChildViewController4() // 必须遵守NestedPageScrollable协议
default:
return nil
}
}
func coverView(in pageViewController: NestedPageViewController) -> UIView? {
return coverView
}
func heightForCoverView(in pageViewController: NestedPageViewController) -> CGFloat {
return 200.0
}
func tabStrip(in pageViewController: NestedPageViewController) -> UIView? {
return customTabStrip // 使用自定义标签栏
}
func heightForTabStrip(in pageViewController: NestedPageViewController) -> CGFloat {
return 50.0
}
func titlesForTabStrip(in pageViewController: NestedPageViewController) -> [String]? {
return nil // 使用自定义标签栏时返回nil
}
}
// MARK: - NestedPageViewControllerDelegate
extension YourViewController: NestedPageViewControllerDelegate {
// 页面横向滚动到指定索引位置的回调方法
func pageViewController(_ pageViewController: NestedPageViewController, didScrollToPageAt index: Int) {
// 页面切换回调
print("当前页面索引: \(index)")
}
// 内容垂直滚动视图的滚动状态变化回调方法
func pageViewController(_ pageViewController: NestedPageViewController, contentScrollViewDidScroll scrollView: UIScrollView, headerOffset: CGFloat, isSticked: Bool) {
// headerOffset: 头部相对contentScrollView顶部的偏移量
// isSticked: 是否处于完全吸顶状态
// 例如:根据滚动状态控制导航栏的显示/隐藏
if isSticked {
// 头部完全吸顶,可以显示导航栏标题
} else {
// 头部未完全吸顶,可以隐藏导航栏标题
}
}
}
方式二:继承方式
import UIKit
import NestedPageViewController
class YourNestedPageViewController: NestedPageViewController {
// MARK: - Properties
private var coverView = YourHeaderView()
private var customTabStrip = YourCustomTabStrip()
// MARK: - View Controllers
private let childControllerTitles = ["标签一", "标签二", "标签三", "标签四"]
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupNestedPageViewController()
}
override func viewDidLayoutSubviews() {
let safeTop = view.safeAreaInsets.top
containerInsets = UIEdgeInsets(top: safeTop, left: 0, bottom: 0, right: 0)
// 采用继承方式时,需要在super之前设置containerInsets
super.viewDidLayoutSubviews()
}
// MARK: - Setup
private func setupNestedPageViewController() {
// 设置数据源
dataSource = self
// 设置代理(继承方式下,可以直接重写代理方法)
delegate = self
}
// MARK: - NestedPageViewControllerDelegate
// 页面横向滚动到指定索引位置的回调方法
override func pageViewController(_ pageViewController: NestedPageViewController, didScrollToPageAt index: Int) {
super.pageViewController(pageViewController, didScrollToPageAt: index)
// 页面切换回调
print("当前页面索引: \(index)")
}
// 内容垂直滚动视图的滚动状态变化回调方法
override func pageViewController(_ pageViewController: NestedPageViewController, contentScrollViewDidScroll scrollView: UIScrollView, headerOffset: CGFloat, isSticked: Bool) {
super.pageViewController(pageViewController, contentScrollViewDidScroll: scrollView, headerOffset: headerOffset, isSticked: isSticked)
// headerOffset: 头部相对contentScrollView顶部的偏移量
// isSticked: 是否处于完全吸顶状态
// 例如:根据滚动状态控制导航栏的显示/隐藏
if isSticked
