我的 View Controller 层次结构如下:

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

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

  • 内容 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
        }
    }
    

    我尝试在我的表 View 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 在调用此方法时尚未创建 ,并且从未设置过 letter 属性。

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

    你知道如何实现这一目标吗?

    最佳答案

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

    以下是我遵循的步骤:

  • 表 View Controller 在 Storyboard中有一个设置为 ContainerViewController 的推送segue。当用户点击一个单元格时它仍然会被执行。
  • 我删除了从容器 View 到 Storyboard中 ContentViewController 的嵌入转场,并在我的类(class)中向该容器 View 添加了一个 IB Outlet。
  • 我为内容 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 。顺便说一句,它曾经是在 UITabBarController 委托(delegate)方法中实例化 UINavigationControllerappDidFinishLaunching:withOptions: 及其子代的方式。

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

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

    10-14 21:09
    查看更多