55758


遷移先のhelloButtonを押したら最初の画面のfastLabel.textに”HelloHello”と表示したいのですが  
エラーになります。。。。なぜなのでしょうか。。。

[![画像の説明をここに入力](https://i.stack.imgur.com/pJUxv.png)](https://i.stack.imgur.com/pJUxv.png)

[code]

    import UIKit
    
    class ViewController: UIViewController {
        static let share = ViewController()
    
        @IBOutlet weak var fastLabel: UILabel!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
        }
    
    
    }
    
[/code]

[code]

    import UIKit
    
    class NextViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
        }
    
        @IBAction func helloButton(_ sender: UIButton) {
            ViewController.share.fastLabel.text = "HelloHello" 
           //eller!! Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
    
        }
    
    
    }
    
[/code]


CC BY-SA 4.0
2019-06-13T02:21:34.083
55739
2019-06-13T12:22:38.100
2019-06-13T04:04:58.690
2238
34541
question
swift xcode
label.textを違う画面から操作したいのですがエラーになる。。。


おそらく、[以前のご質問に対する回答](https://ja.stackoverflow.com/a/55647/13972)なども参考にされた上で、工夫されたのでしょうが、残念ながらviewやview
controllerはシングルトンとして使いまわすのには向いていません。

1番のポイントは、「iOSは画面表示・画面遷移が必要になると、どんどん新しいview
controllerのインスタンスを作っちゃう」という点です。あなたのアプリが`ViewController`クラスの画面を表示している時、その表示を実際に担当しているのはiOSが作成したインスタンスであって、あなたが`share`の中に保存したインスタンスではありません。

また`ViewController()`のようなSwiftのイニシャライザ構文でインスタンス化された場合、iOSは「storyboardを参照してIBOutletを接続する」なんてことは一切やってくれません。従って、`fastLabel`と言うプロパティは存在するものの、それはどこにも接続されておらず値は`nil`、よって`fastLabel`を参照しようとすると
Unexpectedly Found nil の実行時エラーが発生する事になります。

* * *

今回の例のように、「遷移先の画面から、遷移元の画面の要素にアクセスしたい」と言う場合によく使われるのは、デリゲート(delegate)パターンと呼ばれるものです。

まずは遷移元の画面にどんな操作を行いたいのかを`protocol`として宣言しておきます。あなたの例の場合、ラベルの更新さえ出来れば良いようなので、まんまその名の通りの`protocol`を用意しておきます。

[code]

    //### ラベルの更新役ができないといけないこと
    protocol LabelUpdatable: class {
        func updateLabel(_ text: String)
    }
    
[/code]

`updateLabel(_:)`というメソッドの中身がありませんが、これは`LabelUpdatable`と言う役割を果たすために、出来ないといけないメソッドを宣言しているだけだからです。この宣言はプロジェクト内のどの.swiftファイルに置いても良いんですが、一応`NextViewController`と同じ.swiftファイルに同居しているものとしておきます。

`helloButton(_:)`メソッドでは、直接`ViewController`を名指ししてインスタンスを作ったり、シングルトンにアクセスしたりせず、このプロトコルを実装するオブジェクトをプロパティに持っておいて、そのオブジェクトに対してメソッドを呼び出します。

[code]

        //### ラベルの更新役を保持するプロパティ
        weak var labelUpdater: LabelUpdatable?
    
        @IBAction func helloButton(_ sender: UIButton) {
            //### `labelUpdater`のメソッドを呼ぶ
            labelUpdater?.updateLabel("HelloHello")
        }
    
[/code]

ここまでの準備をしておくと、あと必要なことは`ViewController`から`NextViewController`への画面遷移の時に、`ViewController`の側から「ラベルを更新するのは俺」と宣言させることです。

まずは、`ViewController`が`LabelUpdatable`に適合することを宣言しておきます。

[code]

    //### `ViewController`はラベルの更新役ができることを宣言する
    extension ViewController: LabelUpdatable {
        func updateLabel(_ text: String) {
            self.fastLabel.text = text
        }
    }
    
[/code]

セグエで画面遷移する場合には、`prepare(for:sender:)`メソッドが呼ばれますので、その中で「ラベルを更新するのは俺」をコード化します。

[code]

        //### セグエによる画面遷移が起きる時に、自分がラベルの更新役となることを設定する
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if segue.identifier == "ToNext" {
                let destVC = segue.destination as! NextViewController
                destVC.labelUpdater = self
            }
        }
    
[/code]

コード中に`segue.identifier`なんてのが出てきていますが、このコードを動かすためにはセグエにIdentifierを付けてやらないといけません。

[![Segue
Id](https://i.stack.imgur.com/cMnnf.png)](https://i.stack.imgur.com/cMnnf.png)

大文字小文字を間違えたり、余計な空白が入ったりしないように`segue.identifier`に指定するのと全く同じ文字列を設定して下さい。

* * *

少し長くなったので、コードがどうなっていないといけないか確認しておきます。

### ViewController.swift

[code]

    import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var fastLabel: UILabel!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
        }
    
    
        //### セグエによる画面遷移が起きる時に、自分がラベルの更新役となることを設定する
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if segue.identifier == "ToNext" {
                let destVC = segue.destination as! NextViewController
                destVC.labelUpdater = self
            }
        }
    }
    
    //### `ViewController`はラベルの更新役ができることを宣言する
    extension ViewController: LabelUpdatable {
        func updateLabel(_ text: String) {
            self.fastLabel.text = text
        }
    }
    
[/code]

### NextViewController.swift

[code]

    import UIKit
    
    //### ラベルの更新役ができないといけないこと
    protocol LabelUpdatable: class {
        func updateLabel(_ text: String)
    }
    
    class NextViewController: UIViewController {
        //### ラベルの更新役を保持するプロパティ
        weak var labelUpdater: LabelUpdatable?
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
        }
    
        @IBAction func helloButton(_ sender: UIButton) {
            //### `labelUpdater`のメソッドを呼ぶ
            labelUpdater?.updateLabel("HelloHello")
        }
    
    
    }
    
[/code]

お試しの上、何かうまくいかない点、分かりにくい点などあれば、お知らせください。


CC BY-SA 4.0
2019-06-13T12:22:38.100
55758
2019-06-13T12:22:38.100
13972
55739
answer