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

6
我正在学习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 ?? "");
        }

    }
}

谢谢您。

你为什么感觉是错的?这是正确的方式。控制器用于管理数据和这些数据的视图,因此当一个segue推送(或弹出)另一个视图控制器时,在源和目标之间传递一些数据是合理的。 - Jean-Baptiste Yunès
这绝对是在视图控制器之间传递数据的非常普遍的方式,你不应该担心使用它。许多备受尊敬的开发人员都在使用它。当然,并不是唯一的方法,还有其他选择。据我所知,Swift 在这方面并没有带来任何特别之处,因此你也应该查看现有的 Objective C 问题。 - Matt Gibson
4个回答

7
你的问题并不是关于prepareForSegue方法,而是有关视图控制器之间的关系。你的设计“感觉不对”是因为确实如此。问题在于你的笔记撰写视图控制器过多地了解使用它的视图控制器,因为它直接操纵来自调用视图控制器的变量。为了直接操作这个变量,它必须知道调用者的类。
为什么这是个问题呢?这会使得你的笔记撰写视图控制器难以重复使用。如果你正确编写笔记撰写视图控制器,那么你可以在其他应用程序中重复使用它。要使其可重复使用,你需要将笔记撰写视图控制器与调用者解耦-它不能知道谁具体在调用它。
因此,问题就变成了,如果我不知道谁在调用我,我该如何将数据传回给调用者?答案是委托
委托工作原理如下:
  1. You create a protocol which describes a method or methods that the implementor of that protocol will implement. In your case, you could use a protocol like NoteWriterDelegate that implements the method takeNote(note: String).

    protocol NoteWriterDelegate {
        func takeNote(note: String)
    }
    

    Define this in the file along with your note writing view controller.

  2. Your note writer will have an optional pointer to the delegate:

    weak var delegate: NoteWriterDelegate?
    
  3. You need to declare your first view controller as a NoteWriterDelegate:

    class ViewController: UITableViewController, NoteWriterDelegate
    
  4. And then implement the required method in your first view controller:

    func takeNote(note: String) {
        notes.append(note)
    }
    
  5. When you call prepareForSegue in preparation for moving to the note writing view controller, you pass yourself as the delegate:

    destinationViewController.delegate = self
    
  6. In the note writing view controller, when you have a note to pass back to the caller, you call takeNote on the delegate:

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

通过这种方式,您的笔记撰写者只知道它正在与一个NoteWriterDelegate交互。 如果您想在将来重用它,只需将您的笔记撰写者类放入另一个项目中,实现该委托即可,而无需您触碰笔记撰写者类中的代码。


2

我建议在大多数情况下通过prepareForSegue传递数据。这很容易设置并且易于理解。

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

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

委托是一种有效的方法,但我认为它对大多数情况来说过于复杂。它需要更多的设置并且更加抽象。这种抽象在许多视图控制器关系中是不必要的。如果您发现需要重用视图控制器,则始终可以重新设计以后使用委托。


2
我不认为prepareSegue是在视图控制器之间传递数据的理想方式...至少不是直接的。
我分享你对使用prepareForSegue传递值的担忧。源视图控制器不应该知道有关目标视图控制器的任何信息(反过来也是如此)。理想情况下,视图控制器应该是相互独立的岛屿,彼此之间没有可见性。
为了解决故事板似乎鼓励的耦合,我经常使用某种形式的中介者模式来在视图控制器之间传递数据。这里有一篇非常好的博客文章,介绍了如何在故事板周围实现此模式的一个版本:http://coding.tabasoft.it/ios/mediator-pattern-in-swift/。像往常一样,这种模式可能并不适合所有情况,但我觉得它在我的许多过去项目中都是一个很好的解决方案。
基本上,中介者模式在故事板范例中的工作方式是,在每个视图控制器的prepareForSegue方法中,segue对象会传递给中介者对象。视图控制器不关心里面有什么或导航将要去往何处;它只知道自己即将不可见。中介者对象接收到刚传递过来的segue对象(包含源视图控制器和目标视图控制器),然后负责在源视图控制器和目标视图控制器之间传递数据。
使用这种模式,每个视图控制器都没有意识到其他视图控制器的存在。中介者类则必须了解导航路径中视图控制器(以及视图控制器的界面)之间的关系。显然,如果导航发生变化或视图控制器本身发生变化,中介者类将需要进行调整。但是,每个视图控制器都不需要对其他视图控制器产生任何依赖,因此也不需要更新以适应导航路径的更改或其他沿着该导航路径的视图控制器的更改。

0

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

这里有一种传递值和调用视图的替代方法。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接