web-dev-qa-db-ja.com

巻き戻しセグエでデータを渡す

2つのView Controllerを作成しました。データを渡すために、最初から2番目のセグエを作成しました。次に、2番目のView Controllerから最初のView Controllerにデータを渡します。私は多くの同様の質問を経験しましたが、巻き戻しがどのように機能するかに関する知識が不足しているため、それらを実装することはできません。

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
    }
}

これにより、データがView Controller「viewControllerB」のdataPassedに渡されます。たとえば、viewControllerBのデータをViewControllerのdataRecievedに渡したいとします。デリゲートを使用せずに、セグエをほどくだけでこれを行うにはどうすればよいですか。私はSwiftを初めて使用しますが、詳細な説明をお願いします。

30
ebby94

ØyvindHaugeが同じ解決策メソッドに私を打ち負かしましたが、より詳細な答えですでに始めていたので、それも追加します。


2つのView Controllerの名前が次のようになっているとします。

  • マスター/エントリポイント:ViewController(vcA)
  • セカンダリビュー:ViewControllerB(vcB)

例で行ったように、(vcA) -> (vcB)からセグエを設定します

/* 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
    }
}

次にやや厄介なステップは、このメソッドを使用して、データを戻すために使用されるセグエfrom(vcB)to(vcA)also(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 segueを介して、(vcA)のボタンを(vcB)のこの巻き戻しアクションに接続します。

enter image description here

以下は、(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)ソース(ここのUITextFieldDelegateデリゲートは、(vcA)に返され、後者のdataPassedプロパティに割り当てられる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
    }
}

実行例:

enter image description here

61
dfri

これは私がそれをする方法です:

  1. 次のように、View Controller 1でアウトレットを作成します。

    @IBAction func unwindToViewController1(segue: UIStoryboardSegue) {
    
       let foo = segue.sourceViewController.foo
    
       // TODO: Use foo in view controller 1
    }
    
  2. 以下に示すように、View Controller 2(巻き戻し元のVC)を接続します。 vc2の黄色の円から「終了」までドラッグします。 View Controller 1からのIBActionがポップアップするはずです。選択してください。 enter image description here

  3. これで、View Controller 2から巻き戻されるたびに、View Controller 1のunwindToViewController1:メソッドが呼び出されます。

  4. これは、View Controller 2から必要なプロパティを取得する場所です。正しいプロパティを取得するには、segue.sourceViewControllerをカスタムView Controllerサブクラスにキャストする必要があることに注意してください。

11
oyvindhauge

アプリがiOS 9以降をサポートしている場合、prepareForSegueとほぼ同じデータを渡すことができます。 sender プロパティを持つ UIStoryboardUnwindSegueSourceprepare(for:segue:UIStoryboardSegue、sender:Any?)senderプロパティとまったく同じです。

それの使い方:

  1. UnwindToメソッドを作成します。

注:unwindToメソッドの接続は、回答で説明した@ØyvindHaugeと@dfriと同じです。

  1. 巻き戻したいView Controller内で、メソッドをオーバーライドします 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
}
1
OhadM