将数据传递到嵌入在Container View Controller中的View Controller

问题描述 投票:4回答:3

我的视图控制器层次结构如下:

view controller hierarchy

  • 入口点是UINavigationController,其根视图控制器是通常的UITableViewController。表视图显示了一个字母列表。
  • 当用户点击一个单元格时,会触发推送segue,并且视图将转换为ContainerViewController。它包含一个嵌入式ContentViewController,其作用是在屏幕上显示所选字母。

内容视图控制器将要显示的字母存储为属性letter: String,应在其视图按下屏幕之前设置。

class ContentViewController: UIViewController {

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

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

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

class ContainerViewController: UIViewController {

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

我试着在我的表视图控制器中写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属性。

值得一提的是,当segue的目标视图控制器直接在内容视图控制器上设置时 - 相应地更新prepareForSegue()后,这确实有效。

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

ios swift cocoa-touch
3个回答
1
投票

实际上我觉得正确的解决方案是依靠内容视图的程序化实例化,这是我在仔细和彻底的想法之后选择的。

以下是我遵循的步骤:

  • Table View Controller在故事板中将push segue设置为ContainerViewController。当用户点击一个单元格时,它仍会执行。
  • 我将嵌入式segue从容器视图中删除到故事板中的ContentViewController,并在我的班级中向该容器视图添加了一个IB Outlet。
  • 我将故事板ID设置为内容视图控制器,例如... ContentViewController,以便我们可以在适当的时候以编程方式实例化它。
  • 我实现了一个自定义容器视图控制器,如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类中,我实例化并设置了我的内容视图控制器,它被添加到Container的子视图控制器中。这是代码: 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 } } }

这完全适用于完全与内容无关的容器视图控制器。顺便说一句,它曾经是UITabBarController委托方法中实例化UINavigationControllerappDidFinishLaunching:withOptions:及其子项的方式。

我可以看到唯一的缺点:在storyboard上明确显示UI流程。


0
投票

我能想到的唯一方法是添加委托,以便tableViewController实现一个带有一个方法的协议来返回字母;然后你有containerViewController将其childViewController(contentViewController)委托设置为其父级。而且contentViewController最终可以向其代表询问这封信。


0
投票

在您当前的解决方案中,呈现对象本身负责使用“容器”和“内容”,它不必更改,但是这样的解决方案不仅具有您所描述的问题,而且还会产生问题。 “容器”的目的不是很清楚。

查看UIAlertController:您没有直接配置其子视图控制器,使用警报控制器时甚至不应该知道它存在。您正在配置“容器”,而不是配置“内容”,该容器知道内容接口,生命周期和行为,并且不会公开它。按照这种方法,您可以对容器和内容实现适当划分的责任,“内容”的最小曝光允许您更新“容器”而无需更新其使用方式。

简而言之,不要试图从一个地方配置所有内容,而是将其配置为只配置“容器”,并让它在需要的时间和地点配置“内容”。例如。在您描述的场景中,“容器”将在初始化子控制器时为“内容”设置数据。我正在使用“容器”和“内容”而不是ContainerViewControllerContentViewController,因为解决方案并非严格基于控制器,因为你可以用NSObject + UIViewUIWindow替换它。

© www.soinside.com 2019 - 2024. All rights reserved.