首页 > 解决方案 > 根据最近关于用于在 VC 之间传递数据的闭包的问题,​​在 rootVC 中使用 containerVC 时我将如何做同样的事情?

问题描述

我看到了一个最近的赏金问题(如果你想看的话,可以找到链接)关于使用闭包在 VC 之间传递数据,其中一个 VC 嵌入在导航控制器中。虽然使用闭包相当容易,因为两个 VC 之间有一个直接的接触点(以 segue 的形式),但我一直想知道如果不是这种情况,它会如何工作。

例如,考虑以下设置(类似于启发本文的 OG 问题):

RootVC,它有一个计数器 UILabel

一个 subContainer VC,它占据 RootVC 的下半部分,它有一个按钮,按下它应该将 RootVC 上的 UILabel 增加一。

我准备了如下代码(其中一些代码取自 OG 问题):

根VC:

class RootVC: UIViewController {

    var tappedCount: Int = 0

    let pagingContainer: UIView = {
        let view = UIView()
        view.backgroundColor = .white
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var label: UILabel = {
        let label = UILabel()
        label.text = "\(tappedCount)"
        label.textAlignment = .center
        label.font = UIFont(name: "Copperplate", size: 90)
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        view.addSubview(label)
        view.addSubview(pagingContainer)

        pagingContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        pagingContainer.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1).isActive = true
        pagingContainer.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
        pagingContainer.heightAnchor.constraint(equalToConstant: 500).isActive = true

        let pageController = PageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
               addChild(pageController)
               pageController.didMove(toParent: self)
               pageController.view.translatesAutoresizingMaskIntoConstraints = false
               pagingContainer.addSubview(pageController.view)

               pageController.view.heightAnchor.constraint(equalTo: pagingContainer.heightAnchor, multiplier: 1).isActive = true
               pageController.view.widthAnchor.constraint(equalTo: pagingContainer.widthAnchor, multiplier: 1).isActive = true

               pageController.view.topAnchor.constraint(equalTo: pagingContainer.topAnchor).isActive = true
               pageController.view.bottomAnchor.constraint(equalTo: pagingContainer.bottomAnchor).isActive = true
               pageController.view.leadingAnchor.constraint(equalTo: pagingContainer.leadingAnchor).isActive = true
               pageController.view.trailingAnchor.constraint(equalTo: pagingContainer.trailingAnchor).isActive = true


        label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true


 label.bottomAnchor.constraint(equalTo: pagingContainer.topAnchor).isActive = true     
          }
       }

子容器VC:

class SubContainerVC: UIViewController {

    var callback : (() -> Void)?

    let button: UIButton = {
        let button = UIButton()
        button.setTitle("Button!", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
        button.backgroundColor = .green
        return button
    }()

    @objc func buttonPressed(_ sender: UIButton) {
        print("Hello")
        //Pressing this button should increment the label on RootVC by one.
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBlue
        view.addSubview(button)

        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }

}

和 PageViewController 快速文件:

class PageViewController: UIPageViewController {

    lazy var subViewControllers:[UIViewController] = {
        return [SubContainerVC()]
    }()


    init(transitionStyle style:
        UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [String : Any]? = nil) {
        super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
    }



    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()


        dataSource = self
        delegate = self
        setViewControllerFromIndex(index: 0)
    }

    func setViewControllerFromIndex(index:Int) {
        self.setViewControllers([subViewControllers[index]], direction: UIPageViewController.NavigationDirection.forward, animated: true, completion: nil)
    }
}

extension PageViewController: UIPageViewControllerDelegate, UIPageViewControllerDataSource {
    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return subViewControllers.count
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        let currentIndex:Int = subViewControllers.firstIndex(of: viewController) ?? 0
        if currentIndex <= 0 {
            return nil
        }
        return subViewControllers[currentIndex-1]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        let currentIndex:Int = subViewControllers.firstIndex(of: viewController) ?? 0
        if currentIndex >= subViewControllers.count-1 {
            return nil
        }
        return subViewControllers[currentIndex+1]
    }
}

标签: swift

解决方案


您可以将闭包下游注入到SubContainerVC,这将导致闭包执行出现在上游。

类似的东西(只保留相关的 VC 代码):

class SubContainerVC {
    var buttonCallback: () -> Void = { }

    @objc func buttonPressed(_ sender: UIButton) {
        print("Hello")
        buttonCallback()
    }
}

class PageViewController: UIViewController {

    // Note that you don't need the extra closure call for lazy vars 
    lazy var subViewControllers = [SubContainerVC()] {
        didSet {
            // just in case the controllers might change later on
            subViewControllers.forEach { $0.buttonCallback = buttonCallback }
        }
    }

    var buttonCallback: () -> Void = { } {
        didSet { 
            subViewControllers.forEach { $0.buttonCallback = buttonCallback }
        }
    }
}

class RootVC: UIViewController {

    var tappedCount: Int = 0 {
        didSet { 
            label.text = "\(tappedCount)"
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let pageController = PageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
        // this will trigger the `didSet` from PageViewController, resulting
        // in the callback being propagated downstream
        pageController.buttonCallback = { self.tappedCount += 1 }
    }
}

推荐阅读