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

iOS开发:UIPageViewController的两个子视图控制器间数据传递问题

Hey there! Let's work through this data passing problem between your two child view controllers in a UIPageViewController—it's a common scenario once you dig into page-based flows, and we've got a few solid approaches tailored to your setup.

1. Shared Data Source (Great for Bidirectional/Shared State)

If both child VCs need to access or modify the same piece of data, a shared data manager is a clean solution. You can use a singleton (for small apps) or inject an instance through dependency injection (better for testability). Add property observers or notification support to trigger updates when data changes.

// Shared data manager class
class PageDataManager {
    static let shared = PageDataManager() // Singleton instance
    private init() {} // Prevent external initialization
    
    var userInput: String = "" {
        didSet {
            // Notify other VCs when data updates
            NotificationCenter.default.post(
                name: NSNotification.Name("PageDataUpdated"),
                object: nil,
                userInfo: ["newData": userInput]
            )
        }
    }
}

// First child VC: Update the shared data
class FirstChildVC: UIViewController {
    @IBAction func saveButtonTapped(_ sender: UIButton) {
        PageDataManager.shared.userInput = "Data from First VC"
    }
}

// Second child VC: Listen for data updates
class SecondChildVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Subscribe to data change notifications
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleDataUpdate(_:)),
            name: NSNotification.Name("PageDataUpdated"),
            object: nil
        )
    }
    
    @objc private func handleDataUpdate(_ notification: Notification) {
        if let newData = notification.userInfo?["newData"] as? String {
            // Update your UI or process the data here
            print("Received data: \(newData)")
        }
    }
    
    deinit {
        // Clean up observer to avoid memory leaks
        NotificationCenter.default.removeObserver(self)
    }
}

2. Delegate Pattern (Clean Unidirectional Flow)

For explicit one-way data passing (e.g., from the first VC to the second), use a delegate protocol. The parent UIPageViewController acts as the middleman to pass data between children.

// Define a delegate protocol
protocol FirstChildVCDelegate: AnyObject {
    func firstChildDidUpdateData(_ data: String)
}

// First child VC: Hold a weak reference to the delegate
class FirstChildVC: UIViewController {
    weak var delegate: FirstChildVCDelegate?
    
    @IBAction func sendDataButtonTapped(_ sender: UIButton) {
        delegate?.firstChildDidUpdateData("Data from First VC")
    }
}

// Parent UIPageViewController: Implement the delegate and pass data to the second VC
class ParentPageVC: UIPageViewController, UIPageViewControllerDataSource, FirstChildVCDelegate {
    private var secondChildVC: SecondChildVC?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        dataSource = self
        
        // Initialize child VCs
        let firstVC = FirstChildVC()
        firstVC.delegate = self
        secondChildVC = SecondChildVC()
        
        setViewControllers([firstVC], direction: .forward, animated: true)
    }
    
    // MARK: - FirstChildVCDelegate
    func firstChildDidUpdateData(_ data: String) {
        secondChildVC?.receiveData(data)
    }
    
    // MARK: - UIPageViewControllerDataSource
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        return viewController is SecondChildVC ? FirstChildVC() : nil
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        return viewController is FirstChildVC ? secondChildVC : nil
    }
}

// Second child VC: Receive data from the parent
class SecondChildVC: UIViewController {
    func receiveData(_ data: String) {
        // Process or display the data
        print("Received data: \(data)")
    }
}

3. Closures (Concise One-Way Passing)

If you prefer a more lightweight alternative to delegates, closures work great for simple data passing. The parent VC sets up the closure in the first child, which triggers updates to the second child.

// First child VC: Define a closure property
class FirstChildVC: UIViewController {
    var onDataUpdated: ((String) -> Void)?
    
    @IBAction func sendDataButtonTapped(_ sender: UIButton) {
        onDataUpdated?("Data from First VC")
    }
}

// Parent UIPageViewController: Wire up the closure
class ParentPageVC: UIPageViewController, UIPageViewControllerDataSource {
    private var secondChildVC: SecondChildVC?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        dataSource = self
        
        let firstVC = FirstChildVC()
        secondChildVC = SecondChildVC()
        
        // Set up the closure to pass data to the second VC
        firstVC.onDataUpdated = { [weak self] data in
            self?.secondChildVC?.updateWithData(data)
        }
        
        setViewControllers([firstVC], direction: .forward, animated: true)
    }
    
    // MARK: - UIPageViewControllerDataSource
    // (Same data source methods as the delegate example above)
}

// Second child VC: Handle the incoming data
class SecondChildVC: UIViewController {
    func updateWithData(_ data: String) {
        print("Received data: \(data)")
    }
}

Bonus: Trigger Passing On Page Switch Completion

If you need to pass data exactly when the page switch finishes, use the UIPageViewControllerDelegate method didFinishAnimating to detect the transition completion and trigger data transfer.

extension ParentPageVC: UIPageViewControllerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = self // Don't forget to set the delegate!
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        guard completed, let currentVC = pageViewController.viewControllers?.first else { return }
        
        // Pass data from the previous VC to the current one (or vice versa)
        if currentVC is SecondChildVC, let firstVC = previousViewControllers.first as? FirstChildVC {
            secondChildVC?.receiveData(firstVC.someLocalData)
        } else if currentVC is FirstChildVC, let secondVC = previousViewControllers.first as? SecondChildVC {
            // Reverse data passing if needed
            (currentVC as? FirstChildVC)?.receiveData(secondVC.someLocalData)
        }
    }
}

Quick Recommendation

  • Use a shared data manager if both VCs need ongoing access to the same state.
  • Pick delegates or closures for simple, explicit one-way flows.
  • Tie data passing to the didFinishAnimating method if you need it synchronized with page transitions.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:23:26