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

公開日: : 最終更新日:2020/07/09 iOSアプリ開発

記事内に広告を含む場合があります。記事内で紹介する商品を購入することで、当サイトに売り上げの一部が還元されることがあります。

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?」などについては、以下の本が詳しいです!

この記事を書いた人
あきお(@akio0911
派手髪iOSエンジニア。Twitterアカウントは@akio0911。YouTubeチャンネル「あきおチャンネル」にてiOSアプリ開発講座を公開中。著書に「iPhoneアプリ開発レシピ」「cocos2d for iPhoneレッスンノート」など。iOSアプリ開発をテーマとしたオンラインサロン「アプリ道場サロン」を運営。東京・大阪にてアプリ開発講座「アプリクリエイター道場」を主催。

関連記事

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

Twitterで更新情報をゲット!

PAGE TOP ↑