ios - 将数据传递给嵌入在容器 View Controller 中的 View Controller

标签 ios swift cocoa-touch

我的 View Controller 层次结构如下:

view controller hierarchy

  • 入口点是一个UINavigationController,它的 Root View Controller 是一个普通的UITableViewController。表格 View 显示字母列表。
  • 当用户点击一个单元格时,将触发一个push segue,并且 View 会转换到ContainerViewController。它包含一个嵌入式 ContentViewController,其作用是在屏幕上显示所选字母。

Content View Controller 将要显示的字母存储为属性 letter: String,应在将其 View 推送到屏幕之前设置该属性。

class ContentViewController: UIViewController {

    var letter = "-"
    @IBOutlet private weak var label: UILabel!

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        label.text = letter
    }
}

相反,容器 View Controller 不应该知道关于字母的任何信息(内容未知),因为我正试图将其构建为尽可能可重用。

class ContainerViewController: UIViewController {

    var contentViewController: ContentViewController? {
        return childViewControllers.first as? ContentViewController
    }
}

我尝试相应地在我的 TableView Controller 中编写 prepareForSegue() :

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let containerViewController = segue.destinationViewController as? ContainerViewController {
        let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)!
        let letter = letterForIndexPath(indexPath)
        containerViewController.navigationItem.title = "Introducing \(letter)"
        // Not executed:
        containerViewController.contentViewController?.letter = letter
    }
}

但调用此方法时 contentViewController 尚未创建,并且从未设置字母属性。

值得一提的是,当 segue 的目标 View Controller 直接设置在内容 View Controller 上时,这确实有效——在相应地更新 prepareForSegue() 之后。

你知道如何实现吗?

最佳答案

其实我觉得正确的解决方案是依靠内容 View 的程序化实例化,这是我经过深思熟虑后选择的。

以下是我遵循的步骤:

  • TableView Controller 在 Storyboard 中将推送转场设置为 ContainerViewController。当用户点击单元格时,它仍然会执行。

  • 我在 Storyboard中删除了从容器 View 到 ContentViewController 的嵌入转场,并在我的类中向该容器 View 添加了一个 IB 导出。

  • 我为 Content View Controller 设置了一个 Storyboard ID,比如说……ContentViewController,这样我们就可以在适当的时候以编程方式实例化它。

  • 我实现了自定义容器 View Controller ,如 Apple 的 View Controller Programming Guide 中所述.现在我的 ContainerViewController.swift 看起来像(大部分代码安装并删除了布局约束):

    class ContainerViewController: UIViewController {
    
        var contentViewController: UIViewController? {
            willSet {
                setContentViewController(newValue)
            }
        }
    
    
        @IBOutlet private weak var containerView: UIView!
        private var constraints = [NSLayoutConstraint]()
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setContentViewController(contentViewController)
        }
    
        private func setContentViewController(newContentViewController: UIViewController?) {
            guard isViewLoaded() else { return }
            if let previousContentViewController = contentViewController {
                previousContentViewController.willMoveToParentViewController(nil)
                containerView.removeConstraints(constraints)
                previousContentViewController.view.removeFromSuperview()
                previousContentViewController.removeFromParentViewController()
            }
            if let newContentViewController = newContentViewController {
                let newView = newContentViewController.view
                addChildViewController(newContentViewController)
                containerView.addSubview(newView)
                newView.frame = containerView.bounds
                constraints.append(newView.leadingAnchor.constraintEqualToAnchor(containerView.leadingAnchor))
                constraints.append(newView.topAnchor.constraintEqualToAnchor(containerView.topAnchor))
                constraints.append(newView.trailingAnchor.constraintEqualToAnchor(containerView.trailingAnchor))
                constraints.append(newView.bottomAnchor.constraintEqualToAnchor(containerView.bottomAnchor))
                constraints.forEach { $0.active = true }
                newContentViewController.didMoveToParentViewController(self)
            }
        } }
    
  • 在我的 LetterTableViewController 类中,我实例化并设置了我的内容 View Controller ,它被添加到容器的 subview Controller 中。这是代码:

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if let containerViewController = segue.destinationViewController as? ContainerViewController {
            let indexPath = tableView.indexPathForCell(sender as! UITableViewCell)!
            let letter = letterForIndexPath(indexPath)
            containerViewController.navigationItem.title = "Introducing \(letter)"
            if let viewController = storyboard?.instantiateViewControllerWithIdentifier("ContentViewController"),
               let contentViewController = viewController as? ContentViewController {
                contentViewController.letter = letter
                containerViewController.contentViewController = contentViewController
            }
        }
    }
    

这与一个完全与内容无关的容器 View Controller 完美地工作。顺便说一下,它曾经是在 appDidFinishLaunching:withOptions: 委托(delegate)中实例化 UITabBarControllerUINavigationController 及其子项的方式方法。

我能看到的唯一缺点:UI 流程不再明确出现在 storyboard 上.

关于ios - 将数据传递给嵌入在容器 View Controller 中的 View Controller ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36570168/

相关文章:

ios - 为什么 iOS 13 放弃我的布局约束更改?

ios - 将标题设置为 UIBarButtonItem

swift - 当我将 tableView 向下拖动到某个点时如何隐藏它,Swift

ios - 在 subview 中像选取框一样滚动 UILabel

ios - UITableViewController 在 iOS7 中删除行后忽略点击

Objective-C 异步回调

ios - 如何在 Swift 中创建百分比(%)明智的三色渐变?

ios - 通过正则表达式的表情符号数字

ios - 适用于 iOS 的 AWS Cognito 是否允许离线登录?

swift - `sum` 测量序列扩展