iOS 全网最好用的分页控制器

NestedPageViewController

github地址

一个用于 iOS 的嵌套页面视图控制器,提供平滑的滚动协调体验。

平台 iOS | Swift 5.0 | iOS 13.0+ | MIT License | CocoaPods v1.0.0 | SPM Compatible


功能特点

  • 支持头部视图、标签栏和多个子视图控制器
  • 支持内容滚动位置记录
  • 支持局部刷新和全局刷新
  • 支持子页面预加载
  • 支持头部视图拖拽滚动并带动整体
  • 支持自定义标签栏
  • 支持旋转
  • 更多功能请下载demo

功能演示

记录滚动位置 局部刷新 全局刷新
记录滚动位置
局部刷新
全局刷新
头部始终固定不动 头部缩放+隐藏导航栏 显示系统 tabBar
头部始终固定不动
头部缩放+隐藏导航栏
显示系统tabBar
滚到顶部 自定义标签栏1 自定义标签栏2
滚到顶部
自定义标签栏1
自定义标签栏2

系统要求

  • 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

实现原理

实现原理文档


项目起源

前身是 8 年前开发的 HVScrollView 项目,灵感来自腾讯 Bugly 发布的特斯拉组件文章
时光荏苒,8年过去了,我积累了更多的开发经验和技术沉淀,现在将这个想法重新实现并开源,希望能为iOS开发社区提供一个更加完善、易用的嵌套滚动解决方案。NestedPageViewController在保留原有思想精髓的基础上,进一步优化了性能和用户体验,为现代iOS应用提供了更加流畅的页面嵌套滚动效果。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容