使用反向Segue传递数据

36

我创建了两个视图控制器。我创建了从第一个到第二个的segue以传递数据。现在我想从第二个视图控制器向第一个视图控制器传递数据。我查阅了许多类似的问题,但由于我不知道如何取消操作,所以无法实现。

ViewController.swift

class ViewController: UIViewController
{   
    var dataRecieved: String?
    @IBOutlet weak var labelOne: UILabel!
    @IBAction func buttonOne(sender: UIButton)
    {
        performSegueWithIdentifier("viewNext", sender: self)
    }
    override func prepareForSegue(segue: (UIStoryboardSegue!), sender: AnyObject!)
    {

        var svc: viewControllerB = segue.destinationViewController as! viewControllerB
        svc.dataPassed = labelOne.text
    }
}

这将把数据传递到视图控制器“viewControllerB”中的dataPassed。假设现在我想从viewControllerB传递一些数据到ViewController中的dataRecieved。如果只使用Unwind Segue,而不使用代理,我该怎么做?我对Swift相当新,请提供详细解释。


1
在您的反向传递视图控制器中简单实现 prepareForSegue,并访问 destinationViewController,这将是您要反向传递到的视图控制器。您可能希望在Storyboard中为您的反向传递segue指定一个标识符。 - Paulw11
它会报错“没有与标识符'btnSubmitSegue'匹配的segue”。我已经在视图控制器中添加了segue标识符。我只使用一个segue来连接两个视图控制器。我认为Unwind Segue只是回到其上一个视图控制器,没有任何额外的segues?你能详细解释一下吗?如果有代码就更好了 :) - ebby94
您可以像往常一样创建取消转场连线 - 将其拖动到场景中的“退出”图标上。现在,在左侧的对象检查器中,您将看到取消转场连线列在视图控制器、第一响应者和退出图标下面。您可以单击取消转场连线并在右侧的检查器中为其指定标识符。 - Paulw11
3个回答

72

Øyvind Hauge已经用相同的解决方法击败了我,但是由于我已经开始了更详细的答案,所以我也会添加它。


假设您的两个视图控制器命名如下:

  • 主/入口点:ViewController (vcA)
  • 辅助视图:ViewControllerB (vcB)

您可以像在您的示例中一样从(vcA) -> (vcB)设置segue

/* in ViewController.swift */   

// ...

// segue ViewController -> ViewControllerB
override func prepareForSegue(segue: (UIStoryboardSegue!), sender: AnyObject!)
{
    if segue.identifier == "viewNext" {
        let viewControllerB = segue.destinationViewController as! ViewControllerB
        viewControllerB.dataPassed = labelOne.text
    }
}

接下来有点棘手的一步是,使用这种方法,用于将数据从 (vcB) 传回 (vcA) 的Segue也会被添加到(vcA)的源代码中,作为一个@IBAction方法(而不是可能期望的添加到(vcB)的源代码中)。

/* in ViewController.swift */   

// ...

// segue ViewControllerB -> ViewController
@IBAction func unwindToThisView(sender: UIStoryboardSegue) {
    if let sourceViewController = sender.sourceViewController as? ViewControllerB {
        dataRecieved = sourceViewController.dataPassed
    }
}

然后,您需要通过在 (vcB) 中使用手动 Exit 转场,在(vcB)中将一个按钮连接到(vcA)中的此 unwind 动作:

image description

以下是从(vcA)传递文本到(vcB)的完整示例;(可能)通过UITextField修改该文本,最终将(可能)修改后的文本返回给(vcA)


(vcA)源代码:

/* ViewController.swift: Initial view controller */
import UIKit

class ViewController: UIViewController {

    var dataRecieved: String? {
        willSet {
            labelOne.text = newValue
        }
    }
    @IBOutlet weak var labelOne: UILabel!

    @IBAction func buttonOne(sender: UIButton) {
        performSegueWithIdentifier("viewNext", sender: self)
    }

    // set default labelOne text
    override func viewDidLoad() {
        super.viewDidLoad()

        labelOne.text = "Default passed data"
    }

    // segue ViewController -> ViewControllerB
    override func prepareForSegue(segue: (UIStoryboardSegue!), sender: AnyObject!)
    {
        if segue.identifier == "viewNext" {
            let viewControllerB = segue.destinationViewController as! ViewControllerB
            viewControllerB.dataPassed = labelOne.text
        }
    }

    // segue ViewControllerB -> ViewController
    @IBAction func unwindToThisView(sender: UIStoryboardSegue) {
        if let sourceViewController = sender.sourceViewController as? ViewControllerB {
            dataRecieved = sourceViewController.dataPassed
        }
    }
}

(vcB) source (注意这里的UITextFieldDelegate代理仅用于“本地”改变dataPassed属性的值,该值将被返回给(vcA)并分配给后者的dataRecieved属性)

/* ViewControllerB.swift */
import UIKit

class ViewControllerB: UIViewController, UITextFieldDelegate {

    var dataPassed : String?
    @IBOutlet weak var textField: UITextField!

    // set default textField text to the data passed from previous view.
    override func viewDidLoad() {
        super.viewDidLoad()

        textField.text = dataPassed

        // Handle the user input in the text field through delegate callbacks
        textField.delegate = self
    }


    // UITextFieldDelegate
    func textFieldShouldReturn(textField: UITextField) -> Bool {
        // User finished typing (hit return): hide the keyboard.
        textField.resignFirstResponder()
        return true
    }

    func textFieldDidEndEditing(textField: UITextField) {
        dataPassed = textField.text
    }
}

示例执行:

在此输入图像描述


1
非常感谢您详细的解释!那让我更好地理解了 :) - ebby94
@DarkhorseFantasySports 不用担心:这是要设置的新值的默认名称:“如果您实现了willSet观察器,则将新属性值作为常量参数传递。您可以在willSet实现的一部分中指定此参数的名称。如果您没有在实现中编写参数名称和括号,则该参数可使用默认参数名称newValue。”- 来自Swift语言指南 - 属性 - dfrib
1
我想这是最好的答案,作为一个新手iOS开发者,我学到了一些有趣的东西,因为我知道委托是将数据传递回视图控制器的唯一方法,并且还了解了“newValue”的概念!感谢这个答案。 - Swifty Codes
3
在Swift 4.0中,似乎名称已更改。sender.sourceViewController变为sender.source - Damn Vegetables
@dfrib 解释得非常好,表述清晰明了。非常感谢 :) - sugarakis
显示剩余3条评论

15
这是我会的做法:
  1. 在视图控制器1中创建一个插座(outlet),像这样:

    @IBAction func unwindToViewController1(segue: UIStoryboardSegue) {
    
       let foo = segue.sourceViewController.foo
    
       // TODO: Use foo in view controller 1
    }
    
  2. 按照下面所示的方法连接视图控制器2(您要取消连接的vc)。 从vc2中的黄色圆圈拖动到“Exit”。 视图控制器1中的IBAction应该弹出。选择它。 输入图像描述

  3. 现在,每当您从视图控制器2取消连接时,将调用视图控制器1中的unwindToViewController1:方法。

  4. 这是您将从视图控制器2检索所需属性的位置。请注意,您需要将segue.sourceViewController转换为自定义视图控制器子类,以便获取正确的属性。


谢谢!那回答了我的问题 :) - ebby94

4
如果你的应用支持iOS 9+,你可以通过使用UIStoryboardUnwindSegueSource来传递数据,它具有一个sender属性,该属性与prepare(for segue: UIStoryboardSegue, sender: Any?)中的sender属性完全相同。
如何使用:
1.创建一个unwindTo方法。
注意:连接unwindTo方法与@Øyvind Hauge和@dfri在他们的答案中解释的方式相同。
  1. 在你想要返回的视图控制器中,重写方法canPerformUnwindSegueAction(_:from:withSender:)
  2. 在这个方法中,检查类型fromViewController是否是你来自的类型
  3. 如果是,则将sender属性转换为你发送的类型并返回true
  4. 否则,返回false

代码片段(Swift 4.0):

@IBAction func unwindToMyFirstViewController(segue: UIStoryboardSegue) {}

override func canPerformUnwindSegueAction(_ action: Selector, from fromViewController: UIViewController, withSender sender: Any) -> Bool {
    if fromViewController is MyCustomViewController,
        let customType = sender as? MyCustomType {
        return true
    }
    return false
}

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