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

时间:2014-12-13 08:46:55

标签: ios swift segue

我正在尝试学习Swift,我正在尝试开发着名的音符应用程序。

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

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

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 ?? "");
        }

    }
}

谢谢。

4 个答案:

答案 0 :(得分:7)

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

为什么这是一个问题?它使您的笔记编写视图控制器更少可重用。如果您正确编写了笔记编写视图控制器,那么您可以在其他应用程序中重复使用它。为了使其可重用,您需要将音符写入视图控制器与调用者分离 - 它必须不知道究竟是谁调用它。

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

代表团的工作方式如下:

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

    protocol NoteWriterDelegate {
        func takeNote(note: String)
    }
    

    在文件中将其与注释编写视图控制器一起定义。

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

    weak var delegate: NoteWriterDelegate?
    
  3. 您需要将第一个视图控制器声明为NoteWriterDelegate

    class ViewController: UITableViewController, NoteWriterDelegate
    
  4. 然后在第一个视图控制器中实现所需的方法:

    func takeNote(note: String) {
        notes.append(note)
    }
    
  5. 当您致电prepareForSegue以准备将移至注释编写视图控制器时,您将自己作为代理人传递:

    destinationViewController.delegate = self
    
  6. 在便笺写入视图控制器中,当您有一个要回传给来电者的留言时,请在代表上拨打takeNote

    delegate?.takeNote(self.ourTextField?.text ?? "")
    
  7. 通过这种方式,您的笔记作者只知道它正在与NoteWriterDelegate交谈。如果你想在将来重用它,你只需将你的笔记编写器类放到另一个项目中,实现委托,它就可以在你不必触及笔记编写器类中的代码的情况下工作。

答案 1 :(得分:2)

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

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

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

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

答案 2 :(得分:2)

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

我分享了使用prepareForSegue在视图控制器之间传递值的问题。源视图控制器不应该知道目标视图控制器的任何信息(反之亦然)。理想情况下,视图控制器应该是独立的岛屿,彼此之间没有可见性

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

基本上,中介模式在故事板范例中的工作原理是在每个视图控制器的prepareForSegue方法中,segue对象被传递给中介对象。视图控制器不关心内部或下一步导航的内容;它只是知道它不可见。刚刚传递了segue对象(包含源视图和目标视图控制器)的介体负责在源视图控制器和目标视图控制器之间传递数据。

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

答案 3 :(得分:0)

不是''正确的方式,但这是一种正确的方式。尤其是在故事板应用中。

这是传递值并调用视图的另一种方法。

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