MILLEN BOX 2

個人iOSアプリ開発者hollymotoによる勉強の記録。時々雑記。

IBSegueActionについて

こんにちは。hollymoto@anthrgrnwrld です。
Xcode11からIBSegueActionというものが追加されたみたいです。
IBActionのSegue版と思っていればいいのかなーと。
今回はIBSegueActionの使い方と、IBSegueActionに対する思いを残しておきたいなと思います。

目次

1. IBSegueActionとは?

Storyboardから紐付けされたSegueが実行された時の動作をプログラム側に引き渡すものです。
先にも書きましたが、UIButtonなどからコードに紐付けて作成するIBActionのSegue版と考えればいいです。

2. IBSegueActionの使い方

2.1 Ctrl + ドラッグ動作でコードとStoryboardを紐付け

StoryboardのSegueアイコンを選択Ctrl + ドラッグ動作でコードに紐付けします(IBActionと同じ動作)。 f:id:anthrgrnwrld:20200605072930p:plain

ドラッグするとメソッド命名のポップアップが出てきます。これもIBActionと同じですね。
f:id:anthrgrnwrld:20200605073140p:plain:w500

2.2 自動生成されるコードは3パターンから選べる

[None]となってるところの選択すると[None, Sender, Sender and Identifier]の3つが出てきます。
f:id:anthrgrnwrld:20200605073315p:plain:w300

[None, Sender, Sender and Identifier]からどれを選択するのかによって、生成されるコードが変化します。
IBActionSegueが実行される時に受け取れる引数がそれぞれ異なります。
以下に生成されるコード例をメモってみました。

//Noneを選択した時に生成されるコード
@IBSegueAction func segueAction(_ coder: NSCoder) -> DetailViewController? {
    return <#DetailViewController(coder: coder)#>
}

//Senderを選択した時に生成されるコード
@IBSegueAction func actionSegue(_ coder: NSCoder, sender: Any?) -> DetailViewController? {
    return <#DetailViewController(coder: coder)#>
}

//Sender and Identifierを選択した時に生成されるコード
@IBSegueAction func actionSegue(_ coder: NSCoder, sender: Any?, segueIdentifier: String?) -> DetailViewController? {
    return <#DetailViewController(coder: coder)#>
}

[Sender and Identifier]を選択しておけば全て受け取れることになるので、どうなるかわからない時には[Sender and Identifier]にしておくと良いかもしれません。

2.3 returnの適応

そして上記コードでreturn <#DetailViewController(coder: coder)#>となっている箇所を変更します。ここは行先のViewControllerを指定してあげる。
今回の場合、DetailViewControllerに遷移するので以下になります。

//Sender and Identifierを選択した時に生成されるコード
@IBSegueAction func actionSegue(_ coder: NSCoder, sender: Any?, segueIdentifier: String?) -> DetailViewController? {
    return <#DetailViewController(coder: coder)#>
}

エディタ画面で選択してそのままEnterボタンを押すだけでも上記コードに入れ替わると思います。

3. ちょっとだけ実行順番を調べてみた

遷移時って色々処理を実行すると思いますが、IBSegueActionはどのようなタイミングで呼ばれるんでしょうか?ちょっと調べてみました。

今回は手短に実装済みだった以下の3つのメソッドで確認しました。

  • tableView(_:didSelectRowAt:)
  • prepare(for:sender:)
  • IBSegueAction(ここではfunc actionSegueという名前のメソッド)

これら呼び出し口に以下を挿入して呼び出しメソッドをprintしています。

print("\(NSStringFromClass(self.classForCoder)).\(#function) is called!")

結果はこちら。

ListViewController.ListViewController.tableView(:didSelectRowAt:) is called! ListViewController.ListViewController.actionSegue(:sender:segueIdentifier:) is called! ListViewController.ListViewController.prepare(for:sender:) is called!

  1. tableView(_:didSelectRowAt:)
  2. IBSegueAction
  3. prepare(for:sender:)

という順番になりました。
タイミングが重要な場合には気を遣いたいですね。

4. IBSegueActionの美味しいところは?

美味しいところの説明の前に2.3について少し補足。
IBSegueActionのreturnのDetailViewControllerの引数を(coder: coder)にしてますが、これ実は引数を増やして遷移先に値を引き渡せます。ここが美味しいとこです。
え?それ、今までもprepare(for:sender:)実行時に以下みたいに書くことでも出来てたじゃん!!って声が聞こえてきそうですね。

let controller = segue.destination as! DetailViewController
controller.(遷移先のViewController内で宣言されてるvarのプロパティ) = (引き渡す値)

しかし、この方法では(遷移先のViewController内で宣言されてるvarのプロパティ)に値を入れてますよね ?これってつまり値を引き渡す為にpublicなvarの値を準備してるんです。なんかモヤッとしませんか?この先この値が変更されないのであれば尚更です。

IBSegueActionのreturnの遷移先のViewControllerの引数を増やす方法だと、無駄なvarのpublicなプロパティを作らずに済むのです!!

例えば、遷移元で

@IBSegueAction func actionSegue(_ coder: NSCoder, sender: Any?, segueIdentifier: String?) -> DetailViewController? {
    return DetailViewController(coder: coder, text: "example")
}

としたら、遷移先のViewControllerでは以下のように初期化コードを追加してあげます。ほら、init?の中を見て下さい。遷移元からの値が引き渡されてます。そしてその格納先は private letです。美味しい。

private let text: String

init?(coder: NSCoder, text: String) {
    self.text = text
    super.init(coder: coder)
}
    
required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

とここまで書きましたが、本章「美味しいところ」の部分は以下のリンクをめちゃくちゃ参考にして書かれています。こちらの方が詳しく書かれているので確認してみて下さい。

koze.hatenablog.jp

5. まとめ

今までモヤっとしてたことが解決出来ました!これは非常に嬉しい。
積極的にIBSegueAction使って行きたいです!