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

如何通过Segmented Control控制容器内的UIPageViewController?

解决容器视图内UIPageViewController与主VC分段控件的双向联动问题

要实现容器内UIPageViewController和主VC分段控件的双向同步,核心是解决父子视图控制器之间的通信问题。以下是两种适配iOS 12、Swift 5的可行方案:

方法一:代理模式(推荐,代码解耦性更强)

1. 为UIPageViewController定义代理协议

先给你的UIPageViewController创建子类(假设命名为CustomPageViewController),在其中定义代理协议,用来通知主VC当前页面的索引变化:

import UIKit

// 定义代理协议,用于传递页面索引变化
protocol CustomPageViewControllerDelegate: AnyObject {
    func pageViewController(_ pageVC: CustomPageViewController, didSwitchToIndex index: Int)
}

class CustomPageViewController: UIPageViewController {
    // 代理对象,用weak避免循环引用
    weak var pageDelegate: CustomPageViewControllerDelegate?
    // 存储所有子VC的数组
    private var childVCs: [UIViewController] = []
    // 当前页面索引
    private var currentIndex = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 设置自身为数据源和代理
        dataSource = self
        delegate = self
        
        // 从Storyboard初始化子VC(替换成你实际的StoryboardID)
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc1 = storyboard.instantiateViewController(withIdentifier: "FirstVC")
        let vc2 = storyboard.instantiateViewController(withIdentifier: "SecondVC")
        let vc3 = storyboard.instantiateViewController(withIdentifier: "ThirdVC")
        childVCs = [vc1, vc2, vc3]
        
        // 默认显示第一个页面
        setViewControllers([childVCs[0]], direction: .forward, animated: false, completion: nil)
    }
    
    // 提供外部调用方法,跳转到指定索引的页面
    func scrollToPage(at index: Int) {
        guard index >= 0, index < childVCs.count, index != currentIndex else { return }
        let direction: UIPageViewController.NavigationDirection = index > currentIndex ? .forward : .reverse
        setViewControllers([childVCs[index]], direction: direction, animated: true, completion: nil)
        currentIndex = index
    }
}

// 实现UIPageViewController的数据源和代理方法
extension CustomPageViewController: UIPageViewControllerDataSource, UIPageViewControllerDelegate {
    // 返回上一个页面
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let index = childVCs.firstIndex(of: viewController), index > 0 else { return nil }
        return childVCs[index - 1]
    }
    
    // 返回下一个页面
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let index = childVCs.firstIndex(of: viewController), index < childVCs.count - 1 else { return nil }
        return childVCs[index + 1]
    }
    
    // 页面滑动完成后,通知主VC更新分段控件
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        guard completed, let currentVC = pageViewController.viewControllers?.first, let index = childVCs.firstIndex(of: currentVC) else { return }
        currentIndex = index
        pageDelegate?.pageViewController(self, didSwitchToIndex: index)
    }
}

2. 主VC实现代理,处理双向联动

在主VC中,关联分段控件的IBOutlet和点击事件,并设置UIPageViewController的代理:

import UIKit

class MainViewController: UIViewController, CustomPageViewControllerDelegate {
    @IBOutlet weak var mainSegmentedControl: UISegmentedControl!
    // 存储容器内的UIPageViewController实例
    private var pageVC: CustomPageViewController?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 从子VC中找到CustomPageViewController
        for child in children {
            if let pageViewController = child as? CustomPageViewController {
                pageVC = pageViewController
                pageViewController.pageDelegate = self
                break
            }
        }
    }
    
    // 分段控件点击事件:跳转到对应页面
    @IBAction func segmentedControlTapped(_ sender: UISegmentedControl) {
        pageVC?.scrollToPage(at: sender.selectedSegmentIndex)
    }
    
    // 实现代理方法:同步分段控件选中状态
    func pageViewController(_ pageVC: CustomPageViewController, didSwitchToIndex index: Int) {
        mainSegmentedControl.selectedSegmentIndex = index
    }
}

方法二:闭包回调(代码更简洁)

如果觉得代理模式繁琐,可以用闭包来传递页面索引变化:

1. 在UIPageViewController子类中添加闭包属性

class CustomPageViewController: UIPageViewController {
    // 闭包,用于传递当前页面索引
    var indexChangedHandler: ((Int) -> Void)?
    // ... 其他初始化、数据源代码和方法一一致
    
    // 页面滑动完成时调用闭包
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        guard completed, let currentVC = pageViewController.viewControllers?.first, let index = childVCs.firstIndex(of: currentVC) else { return }
        currentIndex = index
        indexChangedHandler?(index)
    }
}

2. 主VC中设置闭包并处理点击事件

class MainViewController: UIViewController {
    @IBOutlet weak var mainSegmentedControl: UISegmentedControl!
    private var pageVC: CustomPageViewController?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        for child in children {
            if let pageViewController = child as? CustomPageViewController {
                pageVC = pageViewController
                // 设置闭包,同步分段控件状态
                pageViewController.indexChangedHandler = { [weak self] index in
                    self?.mainSegmentedControl.selectedSegmentIndex = index
                }
                break
            }
        }
    }
    
    // 分段控件点击事件
    @IBAction func segmentedControlTapped(_ sender: UISegmentedControl) {
        pageVC?.scrollToPage(at: sender.selectedSegmentIndex)
    }
}

关键注意事项

  • 确保在Storyboard中给UIPageViewController设置正确的子类(CustomPageViewController),且子VC的StoryboardID与代码中一致。
  • 闭包和代理中都要使用weak self,避免循环引用导致内存泄漏。
  • iOS 12完全支持以上两种方案,无需额外兼容性处理。

内容的提问来源于stack exchange,提问作者Luiz Fernando Andrade

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 02:48:21