使用UIPageViewController构建通顶的分页视图

作为一名iOS开发者,大家一定都接触和使用过很多分页视图,也一定用过许多优秀的第三方分页视图组件例如JXPagingView等,但是笔者在实际的开发中遇到了全新的需求,如下图所示:

contentView要求要越过tab栏,那么市面上已有的pageView组件便都不能满足这个要求了。 本文将介绍一种contentView越过tab栏的pageView实现思路。

为了能够更快的实现需求,本人决定不使用类似JXPagingView的实现方法,以极简的方式快速实现需求。同时为了方便与同事间的协作,最终选择了UIPageViewController作为底层的滚动视图来承接各个tab下的uiviewcontroller。

有关UIPageViewController的介绍请查阅:blog.csdn.net/HDFQQ188816...

接下来我们直接看如何实现上述效果:

php 复制代码
//1.创建一个uiviewcontroller的实例,并实现相关协议
class BaseViewController:UIViewController,UIPageViewControllerDelegate,UIPageViewControllerDataSource{

//定义UIPageViewController的实例  
    private var pageViewController:UIPageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)

//用于保存contentViewControllers
    private var controllers = [UIViewController]()
    
//获取索引
    private var currentIndex:Int = 0;
    
//定义一个tab视图
    private let categoryView:FindBaseCategoryView = FindBaseCategoryView.init(frame: CGRect.init(x: 0, y: 94, width: screenWidth, height: FitX(value: 51.5)))

//这里定义了一个数据源,当数据源刷新时,初始化pageViewController
    private var titles:[String] = []{
        didSet{
            pageViewController.view.frame = CGRect(x: 0, y: 0, width: (UIScreen.main.bounds.width), height: (UIScreen.main.bounds.height))

            pageViewController.view.backgroundColor = .clear
            pageViewController.delegate = self
            pageViewController.dataSource = self
            pageViewController.view.backgroundColor = .white
            addChild(pageViewController)
            view.addSubview(pageViewController.view)
            pageViewController.didMove(toParent: self)
            view.gestureRecognizers = pageViewController.gestureRecognizers
            pageViewController.setViewControllers([controllers[0]], direction: .forward, animated: false)
            
            self.view.addSubview(categoryView)
            self.categoryView.titles = self.titles
        }

    }
}

下面实现UIPageViewController滑动手势的代理方法,每次代理事件结束就刷新数据源

swift 复制代码
//往左边滑动翻页会走此方法(类似pop回来)

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?{
        let index = self.controllers.firstIndex(of: viewController)
        if (index == 0) || (index == NSNotFound) {
            return nil
        }
        //守护,防止空值在解包时崩溃
        guard let index1 = index else {
            return nil
        }
        return controllers[index1 - 1]
    }
    
   //往左边滑动翻页会走此方法(类似push下去)
   func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?{
        //获取当前控制器索引
        let index = self.controllers.firstIndex(of: viewController)
        if index == NSNotFound {
            return nil
        }
        if (index == self.controllers.count-1) {
            //防止越界,必须要设置
            return nil
        }
        //守护,防止空值在解包时崩溃
        guard let index1 = index else {
            return nil
        }
        return controllers[index1+1]
    }

    
   func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        let controller = pageViewController.viewControllers?[0] ?? UIViewController.init()
        let index = controllers.firstIndex(of: controller)
        categoryView.currentIndex = index
        currentIndex = index ?? 0
        self.setNavStatus()
    }

此外,当点击上面的tab切换page的时候,要控制pageViewController滚动到对应位置,并且刷新数据,代码示例如下:

php 复制代码
override func viewDidLoad() {
        super.viewDidLoad()
        
        viewModel.getData { model in
            self.configControllers(knowTabs: model?.discTabs ?? [])
        }
        
        categoryView.clickClosure = {index in
            if self.currentIndex < index{
                self.pageViewController.setViewControllers([self.controllers[index]], direction: .forward, animated: true)
            }else{
                self.pageViewController.setViewControllers([self.controllers[index]], direction: .reverse, animated: true )
            }
            self.currentIndex = index
    }

上面代码中configControllers()方法是构建controllers和tabs数据的方法,大致实现如下:

swift 复制代码
private func configControllers(knowTabs:[Tab]){
        if knowTabs.isEmpty{
            return
        }
        controllers.removeAll()
        
        var myTitles = [String]()
        for index in 0..<knowTabs.count{
            let model = knowTabs[index]
            let vc = UIViewController.init()
            controllers.append(vc)
            myTitles.append(model.title)
        }
        
        self.titles = myTitles
    }

至此,极简版通顶分页视图的主要代码便实现完了,如果有什么疑问,欢迎留言向我提问。

相关推荐
黑化旺仔11 小时前
【OC】KVO
macos·ios·objective-c·cocoa
HouWan13 小时前
SwiftUI 小技巧:如何读取 SwiftUI Font 的详细信息
ios·swiftui·apple
李游Leo13 小时前
HarmonyOS 7 实战开发 05:把页面做到可上线状态
ios·harmonyos
终端安全笔记19 小时前
描述文件、企业证书、MDM 不是三个名字,是三层
android·ios·智能手机
传奇开心果编程19 小时前
【SwiftUI入门练中学】第1课 从零开始
学习·ui·ios·swiftui·swift
丿Wayne1 天前
iOS Token 刷新实战(Actor 避免并发重复刷新)
ios·swift·token·actor·swift并发
终端安全笔记2 天前
iOS 27 给了租赁一个新工具,但它只认受监督的设备
android·网络·安全·ios
大熊猫侯佩2 天前
独立 App 首发完成 iPhone Duo 展开态适配
ios·swiftui·swift
光影少年2 天前
setImmediate 和 setTimeout(0) 的区别
android·前端·react.js·ios·前端框架
2501_916008892 天前
SSE 和 gRPC 流式接口如何抓包,调试 SSE 与 gRPC 流式接口
网络协议·计算机网络·网络安全·ios·adb·https·udp