【Swift4】次の画面にデータを引き継ぐ５つの方法【Xcode】

画面A（背景ピンク）のテキストフィールドの値を、画面B（背景ブルー）のそれぞれのテキストフィールドに異なる方法で受け渡すサンプルアプリを作ってみます。
<img width="300" alt="v5.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/6a6377ea-3e4d-3755-f3cf-388bba05ff3d.png"><img width="300" alt="v7.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/ace82348-8cd0-370c-c600-4639ca8bb405.png">


#プロジェクトの用意

Xcodeにて「Single View Application」 をテンプレに新規プロジェクトを作成します。

ViewController.swift

```Swift

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var textField:UITextField!
    
    // for back
    @IBAction func goBack(_ segue:UIStoryboardSegue) {}
    
    /* 1 ++++++++++++++ */
    @IBAction func byInstantiateViewController(_ sender:UIButton) {
        let next = storyboard!.instantiateViewController(withIdentifier: "nextView") as? NextViewController
        let _ = next?.view // ** hack code **
        next?.textField1.text = textField.text
        self.present(next!,animated: true, completion: nil)
    }

    /* 2 ++++++++++++++ */
    @IBAction func byInstantiateViewController2(_ sender:UIButton) {
        let next = storyboard!.instantiateViewController(withIdentifier: "nextView") as? NextViewController
        self.present(next!,animated: true, completion: { () in
            next?.textField2.text = self.textField.text
        })
    }

    /* 3 ++++++++++++++ */
    /// セグエによる遷移
    @IBAction func byPerformSegue(_ sender:UIButton) {
        performSegue(withIdentifier: "nextSegue", sender: nil)
    }

    /// セグエ実行前処理
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let next = segue.destination as? NextViewController
        let _ = next?.view
        next?.textField3.text = textField.text
    }
    
    /* 4 ++++++++++++++ */
    @IBAction func byUserDefaults(_ sender:UIButton) {
        UserDefaults.standard.register(defaults: ["textField4":textField.text ?? ""])
        UserDefaults.standard.set(textField.text,forKey:"textField4")
        UserDefaults.standard.synchronize()
        
        let next = storyboard!.instantiateViewController(withIdentifier: "nextView") as? NextViewController
        self.present(next!,animated: true, completion: nil)
    }

    /* 5 ++++++++++++++ */
    @IBAction func byPropertyAccess(_ sender:UIButton) {
        let next = storyboard!.instantiateViewController(withIdentifier: "nextView") as? NextViewController
        self.present(next!,animated: true, completion: nil)
    }

    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
}
```

NextViewController.swift

```Swift
import UIKit

class NextViewController: UIViewController {

    @IBOutlet weak var textField1:UITextField!
    @IBOutlet weak var textField2:UITextField!
    @IBOutlet weak var textField3:UITextField!
    @IBOutlet weak var textField4:UITextField!
    @IBOutlet weak var textField5:UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        
    }

    override func viewWillAppear(_ animated: Bool) {
        /// ローカルストレージ(UserDefaults)から値を取得
        guard let obj = UserDefaults.standard.object(forKey: "textField4") else {
            return
        }
        textField4.text = obj as? String
        
        /// 遷移元ビューコントローラにアクセスして値をセット
        textField5.text = (presentingViewController as? ViewController)?.textField.text

    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

```
##ストーリーボード関連

### ViewController
Controlを押しながら部品からコードに向かってドラッグします。
<img width="2236" alt="x1.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/4028300f-b009-52b0-aa49-d0b1d7bad0eb.png">

### NextViewController
同様に、Controlを押しながら部品からコードに向かってドラッグします。
<img width="2236" alt="x2.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/5d5ad9a1-0fd6-9554-daa3-d53a16b755c4.png">

###セグエ
セグエIDを設定します。今回は*nextSegue*としました。
<img width="1708" alt="v3.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/3665d5af-8f0e-d5c2-cb57-04faca81b9d0.png">

###カスタムクラス
遷移先のビューコントローラクラスに*NextViewController*を割り当てます。
<img width="1708" alt="v4.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/9fba844e-8deb-6c70-1bc3-7f5b5be3c1af.png">

#解説

#### 1.遷移先のビューコントローラのインスタンスを取得してプロパティにセット(その１)

```Swift:ViewController
    let next = storyboard!.instantiateViewController(withIdentifier: "nextView") as? NextViewController
    let _ = next?.view // ** hack code **
    next?.textField1.text = textField.text
    self.present(next!,animated: true, completion: nil)
```
「1-instantiateViewController」ボタン押下時。**`instantiateViewController()`**にて遷移先ビューコントローラのインスタンスを取得してそのプロパティにアクセス。
**`let _ = next?.view`** はアウトレット変数(textField1)をインスタンスさせるためのハックコードです。


#### 2.遷移先のビューコントローラのインスタンスを取得してプロパティにセット(その２)

```Swift:ViewController
    let next = storyboard!.instantiateViewController(withIdentifier: "nextView") as? NextViewController
    self.present(next!,animated: true, completion: { () in
       next?.textField2.text = self.textField.text
    })
```
「2-instantiateViewController2」ボタン押下時。**`instantiateViewController()`**にて遷移先ビューコントローラのインスタンスを取得するとこまでは前項と同じですが、present()のコールバックブロックにてプロパティをセットしています。ハックコードが不要となりますが、テキストフィールドがチラつく感じがちょっと。。。

#### 3.遷移先のビューコントローラのインスタンスを取得してプロパティにセット(その3)

```Swift:ViewController
   performSegue(withIdentifier: "nextSegue", sender: nil)
```
```Swift:ViewController
    /// セグエ実行前処理
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let next = segue.destination as? NextViewController
        let _ = next?.view
        next?.textField3.text = textField.text
    }

```
「3-performSegue」ボタン押下時。セグエ実行前に遷移先インスタンスを取得する方法。**`performSegue()`**は内部にて**`prepare()`**をコールしますのでこれをオーバーライド。アウトレット変数対策のハックコードが必要です。

#### 4.ローカルストレージを経由
```Swift:ViewController
    UserDefaults.standard.register(defaults: ["textField4":textField.text ?? ""])
    UserDefaults.standard.set(textField.text,forKey:"textField4")
    UserDefaults.standard.synchronize()
        
    let next = storyboard!.instantiateViewController(withIdentifier: "nextView") as? NextViewController
    self.present(next!,animated: true, completion: nil)
```
```Swift:NextViewController
    /// ローカルストレージ(UserDefaults)から値を取得
    guard let obj = UserDefaults.standard.object(forKey: "textField4") else {
        return
    }
    textField4.text = obj as? String
```
「4-UserDefaults」ボタン押下時。ローカルストレージに保存して遷移先でロードする方法。そこまでの永続性は必要ないと思いますが参考までに。。。

#### 5.遷移元のビューコントローラのインスタンスを取得
```Swift:NextViewController
    /// 遷移元ビューコントローラにアクセスして値をセット
    textField5.text = (presentingViewController as? ViewController)?.textField.text
```
遷移先から遷移元ビューコントローラのインスタンスにアクセスする方法。UIViewControllerのプロパティ、**`presentingViewController`**には遷移元ビューコントローラのインスタンスが格納されています。ただし、格納タイミングはビュースタック構築後なので、viewDidLoad()内でアクセスしてもnilなので注意が必要です。