NestedPageViewController
一個用于 iOS 的嵌套頁面視圖控制器,提供平滑的滾動協(xié)調體驗。
平臺 iOS | Swift 5.0 | iOS 13.0+ | MIT License | CocoaPods v1.0.0 | SPM Compatible
功能特點
- 支持頭部視圖、標簽欄和多個子視圖控制器
- 支持內容滾動位置記錄
- 支持局部刷新和全局刷新
- 支持子頁面預加載
- 支持頭部視圖拖拽滾動并帶動整體
- 支持自定義標簽欄
- 支持旋轉
- 更多功能請下載demo
功能演示
| 記錄滾動位置 | 局部刷新 | 全局刷新 |
|---|---|---|
![]() 記錄滾動位置
|
![]() 局部刷新
|
![]() 全局刷新
|
| 頭部始終固定不動 | 頭部縮放+隱藏導航欄 | 顯示系統(tǒng) tabBar |
|---|---|---|
![]() 頭部始終固定不動
|
![]() 頭部縮放+隱藏導航欄
|
![]() 顯示系統(tǒng)tabBar
|
| 滾到頂部 | 自定義標簽欄1 | 自定義標簽欄2 |
|---|---|---|
![]() 滾到頂部
|
![]() 自定義標簽欄1
|
![]() 自定義標簽欄2
|
系統(tǒng)要求
- iOS 13.0+
- Swift 5.0+
安裝
Swift Package Manager
[https://github.com/SPStore/NestedPageViewController.git](https://github.com/SPStore/NestedPageViewController.git)
CocoaPods
pod 'NestedPageViewController'
運行:
pod install
注意:如果 CocoaPods 編譯報錯,請在主工程 Targets -> Build Settings -> User Script Sandboxing 改為 No
使用方法
方式一:添加子控制器方式
import UIKit
import NestedPageViewController
class YourViewController: UIViewController {
private var nestedPageViewController = NestedPageViewController()
private var coverView = YourHeaderView()
private var customTabStrip = YourCustomTabStrip()
private let childControllerTitles = ["標簽一", "標簽二", "標簽三", "標簽四"]
override func viewDidLoad() {
super.viewDidLoad()
setupNestedPageViewController()
}
private func setupNestedPageViewController() {
nestedPageViewController.dataSource = self
nestedPageViewController.delegate = self
addChild(nestedPageViewController)
view.addSubview(nestedPageViewController.view)
nestedPageViewController.didMove(toParent: self)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let safeAreaTop = view.safeAreaInsets.top
nestedPageViewController.view.frame = CGRect(
x: 0, y: safeAreaTop,
width: view.bounds.width,
height: view.bounds.height - safeAreaTop
)
}
}
extension YourViewController: NestedPageViewControllerDataSource {
func numberOfViewControllers(in pageViewController: NestedPageViewController) -> Int {
childControllerTitles.count
}
func pageViewController(_ pageViewController: NestedPageViewController, viewControllerAt index: Int) -> (UIViewController & NestedPageScrollable)? {
switch index {
case 0: return YourChildViewController1()
case 1: return YourChildViewController2()
case 2: return YourChildViewController3()
case 3: return YourChildViewController4()
default: return nil
}
}
func coverView(in pageViewController: NestedPageViewController) -> UIView? { coverView }
func heightForCoverView(in pageViewController: NestedPageViewController) -> CGFloat { 200 }
func tabStrip(in pageViewController: NestedPageViewController) -> UIView? { customTabStrip }
func heightForTabStrip(in pageViewController: NestedPageViewController) -> CGFloat { 50 }
func titlesForTabStrip(in pageViewController: NestedPageViewController) -> [String]? { nil }
}
extension YourViewController: NestedPageViewControllerDelegate {
func pageViewController(_ pageViewController: NestedPageViewController, didScrollToPageAt index: Int) {
print("當前頁面索引: \(index)")
}
func pageViewController(_ pageViewController: NestedPageViewController, contentScrollViewDidScroll scrollView: UIScrollView, headerOffset: CGFloat, isSticked: Bool) {
if isSticked { /* 頭部完全吸頂 */ }
else { /* 頭部未吸頂 */ }
}
}
方式二:繼承方式
import UIKit
import NestedPageViewController
class YourNestedPageViewController: NestedPageViewController {
private var coverView = YourHeaderView()
private var customTabStrip = YourCustomTabStrip()
private let childControllerTitles = ["標簽一", "標簽二", "標簽三", "標簽四"]
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
}
override func viewDidLayoutSubviews() {
containerInsets = UIEdgeInsets(top: view.safeAreaInsets.top, left: 0, bottom: 0, right: 0)
super.viewDidLayoutSubviews()
}
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)
}
}
extension YourNestedPageViewController: NestedPageViewControllerDataSource {
func numberOfViewControllers(in pageViewController: NestedPageViewController) -> Int { childControllerTitles.count }
func pageViewController(_ pageViewController: NestedPageViewController, viewControllerAt index: Int) -> (UIViewController & NestedPageScrollable)? {
switch index {
case 0: return YourChildViewController1()
case 1: return YourChildViewController2()
case 2: return YourChildViewController3()
case 3: return YourChildViewController4()
default: return nil
}
}
func coverView(in pageViewController: NestedPageViewController) -> UIView? { coverView }
func heightForCoverView(in pageViewController: NestedPageViewController) -> CGFloat { 200 }
func tabStrip(in pageViewController: NestedPageViewController) -> UIView? { customTabStrip }
func heightForTabStrip(in pageViewController: NestedPageViewController) -> CGFloat { 50 }
func titlesForTabStrip(in pageViewController: NestedPageViewController) -> [String]? { nil }
}
Objective-C 使用方式
OC 工程需橋接,示例參考:Example/NestedPageExample/Examples-OC
性能報告
-
[內存占用]
memory.png -
[CPU 使用率]
cpu.png
實現(xiàn)原理
項目起源
前身是 8 年前開發(fā)的 HVScrollView 項目,靈感來自騰訊 Bugly 發(fā)布的特斯拉組件文章。
時光荏苒,8年過去了,我積累了更多的開發(fā)經驗和技術沉淀,現(xiàn)在將這個想法重新實現(xiàn)并開源,希望能為iOS開發(fā)社區(qū)提供一個更加完善、易用的嵌套滾動解決方案。NestedPageViewController在保留原有思想精髓的基礎上,進一步優(yōu)化了性能和用戶體驗,為現(xiàn)代iOS應用提供了更加流暢的頁面嵌套滾動效果。










