prepareForSegue 是在视图控制器之间传递值的正确方法吗

Posted

技术标签:

【中文标题】prepareForSegue 是在视图控制器之间传递值的正确方法吗【英文标题】:Is prepareForSegue right way of passing value between view controllers 【发布时间】:2014-12-13 08:46:55 【问题描述】:

我正在尝试学习 Swift,我正在尝试开发著名的笔记应用程序。

有一个数组绑定到一个 tableview 和另一个用于添加注释的视图。 在第二个视图 textfieldshouldreturn 事件触发 segue 并返回到 tableview。

我想知道这是否是正确的方法。因为通过这种方式,我正在另一个视图控制器中操作一个变量。我不是 MVC 大师,但我觉得这是错误的。这是我的代码 sn-p:

func textFieldShouldReturn(textField: UITextField) -> Bool 

    self.performSegueWithIdentifier("backSegue", sender: self)
    return true


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 
    if(segue.identifier == "backSegue")
        let navController = segue.destinationViewController as UINavigationController;
        let myController = navController.topViewController as NotesTableViewController;
        if(self.ourTextField?.text != nil || self.ourTextField?.text != "")
            myController.notes.append(self.ourTextField?.text ?? "");
        

    

谢谢。

【问题讨论】:

你为什么感觉它错了?这是正确的方法。控制器用于管理数据和这些数据的视图,因此当一个 segue 推送(或弹出)另一个视图控制器时,在源和目标之间传递一些数据是合理的。 这绝对是一种非常常见的在视图控制器之间传递数据的方式,你不用担心使用它。很多well-respected developers 都这样做。不过,这不是唯一真正的方式,还有其他选择。据我所知,Swift 在这方面并没有什么特别之处,所以你也应该看看existing Objective C question。 【参考方案1】:

您的问题实际上不是关于prepareForSegue,而是视图控制器之间的关系。你的设计“感觉不对”的原因是它确实如此。问题在于您的笔记编写视图控制器对正在使用它的视图控制器了解太多,因为它直接从调用视图控制器操作变量。为了直接操作变量,它必须知道调用者的类。

为什么会出现这个问题?它使您的笔记书写视图控制器的可重用性降低。如果您正确编写了笔记书写视图控制器,那么您可以在其他应用程序中重用它。为了使其可重用,您需要将笔记编写视图控制器与调用者分离——它一定不知道究竟是谁在调用它。

所以问题就变成了,如果我不知道谁给我打电话,我该如何将数据传回给调用者?答案是委托

委派的工作方式如下:

    您创建一个协议,它描述了该协议的实现者将实现的一个或多个方法。在您的情况下,您可以使用像 NoteWriterDelegate 这样的协议来实现方法 takeNote(note: String)

    protocol NoteWriterDelegate 
        func takeNote(note: String)
    
    

    在文件中定义它以及你的笔记书写视图控制器。

    您的笔记编写者将有一个指向委托的可选指针:

    weak var delegate: NoteWriterDelegate?
    

    您需要将您的第一个视图控制器声明为NoteWriterDelegate

    class ViewController: UITableViewController, NoteWriterDelegate
    

    然后在你的第一个视图控制器中实现所需的方法:

    func takeNote(note: String) 
        notes.append(note)
    
    

    当您调用prepareForSegue 以准备移动笔记书写视图控制器时,您将自己作为委托传递:

    destinationViewController.delegate = self
    

    在笔记编写视图控制器中,当你有笔记要传回给调用者时,你在委托上调用takeNote

    delegate?.takeNote(self.ourTextField?.text ?? "")
    

通过这种方式,您的笔记作者只知道它正在与NoteWriterDelegate 交谈。如果你想在将来重用它,你只需将你的笔记编写器类放到另一个项目中,实现委托,它就可以工作,而无需你接触笔记编写器类中的代码。

【讨论】:

【参考方案2】:

我建议在大多数情况下通过 prepareForSegue 传递数据。它的设置非常简单且易于理解。

但是,我建议不要直接在目标视图上更新 UI 元素(标签、文本字段等)。在我看来,这是一种糟糕的耦合,会产生很多问题。

相反,在目标视图控制器上创建一个或多个属性,调用者可以在 prepareForSegue 中设置该属性以将数据传递给它。这些应该是专门用于传递数据的特殊用途属性。然后目标视图控制器负责使用这些属性中的数据来更新其 UI 或内部状态。

委派是一种有效的方法,但我发现在大多数情况下它过于矫枉过正。它需要更多的设置并且更抽象。在很多视图控制器关系中不需要这种抽象。如果您发现需要重用视图控制器,您可以随时重构以在以后使用委托。

【讨论】:

【参考方案3】:

我不认为 prepareSegue 是在视图控制器之间传递数据的理想方式...至少不是直接的。

我和你一样担心使用prepareForSegue 在视图控制器之间传递值。源视图控制器不应该知道任何关于目标视图控制器的信息(反之亦然)。理想情况下,视图控制器应该是独立的岛,彼此之间不可见

为了解决故事板似乎鼓励的耦合,我经常使用某种形式的mediator pattern 在视图控制器之间传递数据。这是一篇关于如何围绕故事板实现此模式版本的非常好的博客文章:http://coding.tabasoft.it/ios/mediator-pattern-in-swift/。与往常一样,这种模式可能并不适合所有情况,但我觉得在我过去的许多项目中,它都是一个很好的解决方案。

基本上,中介者模式在故事板范式中的工作方式是,在每个视图控制器的 prepareForSegue 方法中,将 segue 对象传递给中介者对象。视图控制器不关心里面有什么或者导航接下来要去哪里;它只知道它即将不可见。刚刚传递了 segue 对象(包含源视图控制器和目标视图控制器)的中介者负责在源视图控制器和目标视图控制器之间传递数据。

使用这种模式,每个视图控制器都幸福地不知道另一个的存在。另一方面,中介类必须知道导航路径中视图控制器(和视图控制器的接口)之间的关系。显然,如果导航发生变化,或者视图控制器本身发生变化,中介类将需要调整。 然而,每个视图控制器不需要相互依赖,因此不需要更新以适应导航路径的变化或沿该导航路径对其他视图控制器的变化。

【讨论】:

【参考方案4】:

这不是“正确”的方式,但它是正确的方式。特别是在故事板应用程序中。

这是传递值和调用视图的另一种方式。

var myNewVC = NewViewController()
myNewVC.data = self
navigationController?.presentViewController(myNewVC, animated: true, completion: nil)

【讨论】:

以上是关于prepareForSegue 是在视图控制器之间传递值的正确方法吗的主要内容,如果未能解决你的问题,请参考以下文章

swift - 通过 prepareforsegue 在 2 个视图控制器之间添加值

prepareForSegue 在 iOS 7 上无法获取目标视图控制器

如果我使用 prepareForSegue 传递数据,则无法嵌入导航控制器

将选项卡栏控制器嵌入导航控制器时的 prepareForSegue

来自一个视图控制器的两个segue,prepareForSegue

PrepareforSegue 不显示目标视图控制器