【Xcode】モーダル表示で遷移先へ値を渡す時のハマりポイント

公開日: : iPhoneアプリ開発

20160210-140020.jpg

モーダル表示で遷移先の画面へ値を渡す際、最初はハマりやすいポイントについて解説したいと思います。

    

例えば、以下のような画面遷移を考えたとします。

20160210-140327.jpg

1つ目の画面でテキストフィールドに文字を入力してボタンを押すと、モーダル表示で次の画面に遷移し、入力した文字がラベルに表示されるという動きです。

    

1つ目の画面では、prepareForSegueで次の画面に入力テキストを受け渡します。

class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!
    
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        guard let second = segue.destinationViewController as? SecondViewController else {
            return
        }

        guard let text = self.textField.text else {
            return
        }
        
        second.text = text
    }
}

    

そして2つ目の画面ではviewDidLoadにて、前の画面から受け渡されてきた入力テキストをラベルに表示します。

class SecondViewController: UIViewController {
    
    var text : String = ""

    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.label.text = text
    }
}

    

これでうまく動きそうですが、実行してみると2つ目の画面のラベルに何も表示されません。

なぜでしょうか?

    

1つ目の画面のprepareForSegueに以下のコードを追加してみましょう。

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    
    // 追加。destinationViewControllerの中身の型を調べる
    print("type = \(segue.destinationViewController.dynamicType)")
    
    guard let second = segue.destinationViewController as? SecondViewController else {
        return
    }

    guard let text = self.textField.text else {
        return
    }
    
    second.text = text
}

    

ちなみにdynamicTypeを使うと、インスタンスが実際に属するクラスを表すオブジェクトを得ることができます。

インスタンスに対してdynamicTypeという問い合わせを行うと、実際に属するクラスを表すオブジェクト(便宜的にクラスオブジェクトと呼びます)を値として得ることができます。

引用元 : 詳解 Swift 改訂版

    

そして再び実行すると、1つ目の画面でボタンをタップした際に以下のような出力がなされます。

type = UINavigationController

なんと、destinationViewControllerの中身がSecondViewControllerではなくてUINavigationControllerになっています!

    

これは何故かというと、1つ目の画面からUINavigationControllerに対してセグエを接続しているからなんですね。

20160210-142431.jpg

だから最初の

guard let second = segue.destinationViewController as? SecondViewController else {
    return
}

でas?によるダウンキャストに失敗して、次の画面にデータが受け渡されなかったわけです。

    

なので、1つ目の画面のprepareForSegueでは

  • まずセグエで接続されているUINavigationControllerを取得する
  • 次に、UINavigationControllerにぶら下がっているSecondViewControllerを取得する

という2段階の手順を踏まなければならないわけです。

これに従ってコードを書いてみましょう。

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    
    // まずセグエで接続されているUINavigationControllerを取得する
    guard let nav = segue.destinationViewController as? UINavigationController else {
        return
    }
    
    // UINavigationControllerにぶら下がっているSecondViewControllerを取得する
    guard let second = nav.topViewController as? SecondViewController else {
        return
    }

    guard let text = self.textField.text else {
        return
    }
    
    second.text = text
}

これで無事、2つ目の画面に入力テキストが表示されるようになります!

    

@akio0911はこう思った。

今日解説した部分は、入門書などを読みながら学習していく過程でハマりやすいポイントの1つだと思います。

    

ちなみに解説の中で出てきた「guard」「as?」などについては、以下の本が詳しいです!

関連記事

この記事が気に入ったら「いいね!」しよう

follow us in feedly

Feedlyで最新記事を購読

Twitterで更新情報をゲット!

LINEでご感想・ご要望お送りください!
(スマホでLINEを起動 > 友だち追加 > QRコード)

PAGE TOP ↑