You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需Storyboard创建UIPageViewController,基于API数据生成带页码页面

嘿,这就给你搞定!下面是纯代码实现带PageControl的UIPageViewController,同时处理API数据的完整方案,完全不需要Storyboard:

步骤1:解析API数据并获取datashow数组长度

首先我们需要把API返回的JSON数据解析成Swift模型,然后直接取数组的count属性就能得到页面总数(这里是3)。

1.1 定义数据模型

用Codable协议来简化JSON解析:

struct DataItem: Codable {
    let name: String
    let age: Int
    let city: String
    let type: String
}

struct APIResponse: Codable {
    let datashow: [DataItem]
    let link: [String: Any]? // 适配API返回的空对象字段
}

1.2 解析数据并获取页面数量

假设你已经通过网络请求拿到了API返回的Data对象,解析代码如下:

// 这里用模拟数据示例,实际项目替换成真实网络请求的data
let mockAPIData = """
{
    "datashow": [
        { "name": "seena", "age": 34, "city": "u.k", "type": "human" },
        { "name": "cat", "age": 3, "city": "u.s.a", "type": "animal" },
        { "name": "parrot", "age": 31, "city": "south africa", "type": "bird" }
    ],
    "link": {}
}
""".data(using: .utf8)!

do {
    let response = try JSONDecoder().decode(APIResponse.self, from: mockAPIData)
    let dataItems = response.datashow
    let totalPages = dataItems.count // 这里就是我们需要的3
    // 后续用totalPages创建页面控制器
} catch {
    print("JSON解析失败: \(error.localizedDescription)")
}
步骤2:纯代码创建UIPageViewController带PageControl

接下来我们创建主控制器,在里面初始化UIPageViewController,实现数据源、代理逻辑,同时添加底部的PageControl。

class MainViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
    
    private var pageViewController: UIPageViewController!
    private var dataItems: [DataItem] = []
    private let pageControl = UIPageControl()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        // 先解析数据(这里复用上面的模拟数据逻辑)
        parseMockAPIData()
        // 初始化页面控制器
        setupPageViewController()
        // 添加底部页码指示器
        setupPageControl()
    }
    
    private func parseMockAPIData() {
        let mockAPIData = """
        {
            "datashow": [
                { "name": "seena", "age": 34, "city": "u.k", "type": "human" },
                { "name": "cat", "age": 3, "city": "u.s.a", "type": "animal" },
                { "name": "parrot", "age": 31, "city": "south africa", "type": "bird" }
            ],
            "link": {}
        }
        """.data(using: .utf8)!
        
        do {
            let response = try JSONDecoder().decode(APIResponse.self, from: mockAPIData)
            self.dataItems = response.datashow
        } catch {
            print("解析失败: \(error)")
        }
    }
    
    // 初始化UIPageViewController
    private func setupPageViewController() {
        pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
        pageViewController.dataSource = self
        pageViewController.delegate = self
        
        // 设置首个页面(展示第一个datashow元素的详情)
        if let firstPage = createPageVC(at: 0) {
            pageViewController.setViewControllers([firstPage], direction: .forward, animated: false, completion: nil)
        }
        
        // 将页面控制器添加到当前视图
        addChild(pageViewController)
        view.addSubview(pageViewController.view)
        pageViewController.view.frame = view.bounds
        pageViewController.didMove(toParent: self)
    }
    
    // 创建单个页面的详情控制器
    private func createPageVC(at index: Int) -> UIViewController? {
        guard index >= 0 && index < dataItems.count else { return nil }
        
        let item = dataItems[index]
        let pageVC = UIViewController()
        pageVC.view.backgroundColor = .white
        // 给视图打标签,方便后续判断当前页面索引
        pageVC.view.tag = item.hashValue
        
        // 构建详情内容(这里用垂直栈视图展示所有字段)
        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.spacing = 18
        stackView.alignment = .center
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        let nameLabel = UILabel()
        nameLabel.text = "Name: \(item.name)"
        nameLabel.font = .systemFont(ofSize: 22, weight: .bold)
        
        let ageLabel = UILabel()
        ageLabel.text = "Age: \(item.age)"
        ageLabel.font = .systemFont(ofSize: 18)
        
        let cityLabel = UILabel()
        cityLabel.text = "City: \(item.city)"
        cityLabel.font = .systemFont(ofSize: 18)
        
        let typeLabel = UILabel()
        typeLabel.text = "Type: \(item.type)"
        typeLabel.font = .systemFont(ofSize: 18)
        
        stackView.addArrangedSubview(nameLabel)
        stackView.addArrangedSubview(ageLabel)
        stackView.addArrangedSubview(cityLabel)
        stackView.addArrangedSubview(typeLabel)
        
        pageVC.view.addSubview(stackView)
        
        // 用AutoLayout固定栈视图在屏幕中心
        NSLayoutConstraint.activate([
            stackView.centerXAnchor.constraint(equalTo: pageVC.view.centerXAnchor),
            stackView.centerYAnchor.constraint(equalTo: pageVC.view.centerYAnchor)
        ])
        
        return pageVC
    }
    
    // 设置底部PageControl
    private func setupPageControl() {
        pageControl.numberOfPages = dataItems.count
        pageControl.currentPage = 0
        pageControl.tintColor = .systemBlue
        pageControl.currentPageIndicatorTintColor = .systemIndigo
        pageControl.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(pageControl)
        
        // 固定在安全区底部中央
        NSLayoutConstraint.activate([
            pageControl.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
            pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])
    }
    
    // MARK: - UIPageViewController数据源方法
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let currentIndex = dataItems.firstIndex(where: { $0.hashValue == viewController.view.tag }) else {
            return nil
        }
        return createPageVC(at: currentIndex - 1)
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let currentIndex = dataItems.firstIndex(where: { $0.hashValue == viewController.view.tag }) else {
            return nil
        }
        return createPageVC(at: currentIndex + 1)
    }
    
    // MARK: - UIPageViewController代理方法(更新PageControl选中状态)
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        guard completed, let currentVC = pageViewController.viewControllers?.first,
              let currentIndex = dataItems.firstIndex(where: { $0.hashValue == currentVC.view.tag }) else {
            return
        }
        pageControl.currentPage = currentIndex
    }
}
最后一步:设置根控制器

因为不用Storyboard,你需要在SceneDelegate(iOS 13+)或者AppDelegate里设置窗口的根控制器:

// SceneDelegate.swift示例
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    let window = UIWindow(windowScene: windowScene)
    window.rootViewController = MainViewController()
    self.window = window
    window.makeKeyAndVisible()
}

这样运行后,就能看到带PageControl的3个页面,首个页面会展示seena的详情信息,左右滑动可以切换页面,PageControl也会同步更新选中状态。

内容的提问来源于stack exchange,提问作者Christeena Joseph

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:32:52