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と同じ動作)。
ドラッグするとメソッド命名のポップアップが出てきます。これもIBActionと同じですね。
2.2 自動生成されるコードは3パターンから選べる
[None]となってるところの選択すると[None, Sender, Sender and Identifier]の3つが出てきます。
[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!
- tableView(_:didSelectRowAt:)
- IBSegueAction
- 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") }
とここまで書きましたが、本章「美味しいところ」の部分は以下のリンクをめちゃくちゃ参考にして書かれています。こちらの方が詳しく書かれているので確認してみて下さい。
5. まとめ
今までモヤっとしてたことが解決出来ました!これは非常に嬉しい。
積極的にIBSegueAction使って行きたいです!