IBSegueAction の使い方
Xcode 11から IBSegueAction
が追加された。利用可能なOSは iOS 13, macOS 10.15以降
この新機能は Segue で接続し呼び出す ViewController への必須パラメータ渡しの点でメリットがある。
既存実装
prepare(for:sender:)
だと
func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let viewController = segue.destination as? MyViewController { viewController.text = "example" } }
という様に、渡ってくる ViewController は init?(coder: NSCoder)
で既に初期化済のインスタンスで、プロパティ経由でパラメータ渡しを行うしかなかった。
そのためパラメータは private
にする事ができず internal
や public
にする必要があり、また外部から変更可能な var
にする必要があった。
Optional の場合は以下
class DestinationViewController: UIViewController { var text: String? }
Non Optional の必須パラメータの場合は以下の様に ImplicitlyUnwrappedOptional で実装する事になる。
class DestinationViewController: UIViewController { var text: String! }
これに加えて ViewController 側での値変更がどのタイミングで呼ばれても良い様に考慮すると(一例としては)以下の様な実装になる。
class DestinationViewController: UIViewController { @IBOutlet weak var label: UILabel! var text: String? { didSet { updateLabel() } } override func viewDidLoad() { super.viewDidLoad() updateLabel() } private func updateLabel() { if isViewLoaded { label.text = text } }
IBSegueAction
@IBSegueAction
attributes を使うと、IBで接続と呼び出しが可能な ViewController を返すメソッドを定義でき、任意のイニシャライザを使って ViewController を作成する事ができる。
class SourceViewController: UIViewController { @IBSegueAction func makeDestinationViewController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> DestinationViewController? { return DestinationViewController(coder: coder, text: "example") } }
ViewController 側のパラメータは private
にする事が可能で let
にする事もできる。
初期化に必要なパラメータが変更不要で外部公開不要なプライベートな変数な場合、その役割通りに正しく宣言できる。
class DestinationViewController: UIViewController { @IBOutlet weak var label: UILabel! 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") } override func viewDidLoad() { super.viewDidLoad() label.text = text } }
利用方法
IBでの設定はちょっと分かり辛い。
まずこれまでのIBと同じ様に Button などから別の ViewController へ Segue での画面遷移を設定する。
作成した Segue を選択しインスペクタの一番右のタブに切り替えると instantiation
という項目がXcode 11では増えており、ここから Control+Drag で ViewController に線を伸ばすとコードで定義した makeDestinationViewControllerWithCoder
を選択し接続する事ができる様になっている。
まとめ
外部に出さない変数や変更しない変数をその通りに正しく宣言する事ができるのでGood。
大した事ではないけど独自イニシャライザの宣言によって、既存のイニシャライザ required init?(coder: NSCoder)
の空実装が必要な点だけちょっと面倒。
追記
ViewController をカスタムイニシャライザで作成可能にするためのメソッドが UIStoryboard
にも追加されていた。
https://developer.apple.com/documentation/uikit/uistoryboard/3213989-instantiateviewcontroller
使い方は以下の様になる
let viewController = storyboard.instantiateInitialViewController { (coder) -> DestinationViewController? in return DestinationViewController(coder: coder, text: "example") }
let viewController = storyboard.instantiateViewController(identifier: "identifier") { (coder) -> DestinationViewController? in return DestinationViewController(coder: coder, text: "example") }
UIStoryboard
からインスタンス化する場合も同じく外部に出さない変数や変更しない変数をその通りに正しく宣言する事ができるのが肝だと思う。