繁体   English   中英

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

[英]Pass data to View Controller embedded inside a Container View Controller

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

视图控制器层次

  • 入口点是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
    }
}

我试着在Table 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属性。

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

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

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

以下是我遵循的步骤:

  • Table View Controller在storyboard中有一个push segue设置为ContainerViewController 当用户点击一个单元格时,它仍会执行。

  • 我将embed segue从Container View中删除到了storyboard中的ContentViewController ,并在我的类中为该Container View添加了一个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 } } } 

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

我可以看到唯一的缺点:UI流程更长时间显示在故事板上

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

在当前解决方案的展示对象本身负责的工作都与“容器”和“内容”,它并没有改变 ,但这样的解决方案不仅具有像你描述的一个问题,也使得“容器”的目的不是很清楚。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM