Segue
#iOSDC_2019
概要
Segue とは?
segue とは、Storyboard ファイル上で、2つのViewController 間の遷移を定義するもの であり、UIStoryboardSegue のことを指す。遷移元はボタンやテーブルセル、ジェスチャーの場合もあり、遷移先は ViewController になる。最終的に新しい ViewController を表示するが、遷移先から遷移元に戻るための unwind segue というものも存在する。
基本的には、segue を programmatically に呼び出す必要はなく、ランタイムに UIKitによってロード&要素のとの紐付けが行われる。
https://gyazo.com/3d9d246cafa6fb341c3f0bae97fb395c
Segue 利用のメリット
コードをほとんど書かずに遷移を実現できる
非プログラマでもメンテできる
画面遷移が storyboard で完結する
Segue の種類
大別すると、主に下記のような種類がある。
Standard Segue
通常の segue。あるViewController のUI要素から別のViewControllerへの遷移時に利用する作成は Interface Builder 上で行う。遷移元のUI要素からOption+ドラッグ で遷移先のViewControllerに結線する。
結線すると、下記のようなメニューが現れる。
https://gyazo.com/a3343064fd33aedf3a45760f3ab5ec20
このメニューでは、作成する Segue による遷移元と遷移先の 関連の種類 を選択する。
Selection vs Accessory
Selection Segue
遷移元が UITableViewCell や UIButton などのUI要素の場合
Accessory Action
遷移元がUI要素の Accessory Viewの場合
Adaptive vs Non-Adaptive
Adaptive Segue
遷移時の環境に応じて振る舞いを自動的に変更する
例えば、Show による遷移の振る舞いは、遷移先/元の ViewController によって変化する
Non-Adaptive Segue
Adaptive segue をサポートしていない環境用
iOS 7 (!) でアプリを実行したい時に採用する
Adaptive segue における遷移の振る舞いの種別は以下の通り (Xcode 6 から名称が変更になっている)。
Show (Push)
show(_:sender:) メソッドを利用する
遷移先の ViewController を primary context で描画する
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621377-showviewcontroller
Show Detail (Replace)
showDetailViewController(_:sender:) メソッドを利用する
遷移先の ViewController を secondary context で描画する
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621432-showdetailviewcontroller
Present Modally
指定された presentation style (modalPresentationStyle), transition sytle (modalTransitionStyle) を利用して ViewController をモーダル表示する
Present as Popover
水平方向が regular な環境では popover、compact な環境では overFullScreen 表示を利用する
https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html
Tips: show(_:sender:), showDetailViewController(_:sender:) のデフォルト実装
どちらも、targeViewControllerForAction:sender:を呼び出す。targeViewControllerForAction:sender:は、View hierarchy を辿っていき、指定した action method (selector) をハンドリングできる ViewController を探索し、存在すればそれ自身を、存在しなければ nil を返す。また、このメソッドをコールした対象の ViewController 自身が指定した action method をハンドリングするのであれば、自身を返す。
もっと簡単にいうと、View hierarchy から show(_:sender:)/showDetailViewController(_:sender:) をオーバーライドした ViewController を見つけ出し、それを取得する。そして、その取得した ViewController のshow(_:sender:)/showDetailViewController(_:sender:) を呼び出し、画面遷移する。targeViewControllerForAction:sender:が nil を返した場合、ウインドウの root view controller を利用して ViewController をモーダル表示する。
これらのメソッドは iOS8 から追加されたもので、それ以前だと、UISplitViewController や UINavigationController のような親 ViewController の子 ViewController にて画面遷移/表示を行う場合には、わざわざ親 ViewController を呼び出して実行していたが、このメソッドは自動的に親を探索してくれるのでそのようなことをしなくて良くなったようだ。
show(_:sender:)/showDetailViewController(_:sender:) の違いは、前者は primary, 後者は secondary として ViewController を描画する、と説明がある。この primary/secondary という概念は UISplitViewController に埋め込まれた ViewController にのみ存在する概念で、後者は実質 UISplitViewController に埋め込まれた ViewController にしか関係しないとのこと。
iOS8からUIViewControllerに追加されたshowViewController()とshowDetailViewController()について - Qiita
uisplitviewcontroller - iOS - Split View Controller - How do I get a pointer (reference) to the Detail View Controller from inside the Master View Controller? - Stack Overflow
Embed Segue
親となる ViewController から Container View (子ViewController) への遷移時に利用する。
https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/Art/container_view_embed_2x.png
Unwind Segue
遷移先の ViewController から遷移元の ViewController に戻る時に利用する
https://dev.classmethod.jp/smartphone/iphone/ios_unwind-segue/
Custom Segue
UIStoryboardSegue を自作して利用することもできる。perform() を override して処理を記述しておくと、その処理が遷移発生時に OS によって自動的に発火する。ここにはどんな処理でも大体書けてしまう。
code:swift
class MySegue: UIStoryboardSegue {
override func preform() {
// ...
}
}
ランタイムの挙動
プログラム上から segue の遷移を実行したい場合は、performSegue(withIdentifier:sender:) を実行する (Interface Builder 上で設定した Identifier を引数に渡す必要がある)。すると、遷移元の ViewController の2つのメソッドが、UIKit によって呼び出される。
shouldPerformSegue(withIdentifier:sender:)
segue を実行すべきかどうか の判断ができるタイミング
false を返すとsegueの実行をしない
prepare(for segue:sender:)
遷移元から 遷移先のViewControllerへ情報を受け渡すことができる タイミング
UIStoryboardSegue オブジェクト内に、遷移先への参照や、segue関連の情報が含まれている
https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/Art/VCPG_displaying-view-controller-using-segue_9-4_2x.png
その後、Segue の perform() メソッドが呼び出され、遷移が発火する。
View Controller Programming Guide for iOS - Using Segues
遷移先に任意の情報を受け渡す
iOS 13 未満
segue による遷移時、遷移先の ViewController に情報を渡したい場合は、これまでだと下記のように記述する必要があった。
code:FirstViewController.swift
// プロパティが受付可能であることを示すプロトコル
protocol SomePropsAcceptable {
func accept(prop: SomePropType)
}
class FirstViewController: UIViewController {
// 遷移を実行するアクション
func someAction() {
let prop: SomePropType = /* ... */
self.performSegue(withIdentifier: "MySegue", sender: prop)
}
// 遷移直前の割り込み処理
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let acceptable = segue.destination as? SomePropAcceptable {
if let prop = sender as? SomePropType {
acceptable.accept(prop: prop)
}
}
}
}
code:SecondViewController.swift
class SecondViewController: UIViewController {
// ここが mutable かつ implicity unwrapped optional になってしまう
var someProp: SomePropType!
}
extension HogeViewController: SomePropsAcceptable {
func accept(prop: SomePropType) {
self.someProp = prop
}
}
Segue Action
iOS 13 から Segue Action と呼ばれる @IBSegueAction という Attribute が追加された。これにより、segue の遷移先の ViewController をコード上から直接生成できる ようになる。
A view controller method annotated with the new @IBSegueAction attribute can be used to create a segue’s destination view controller in code, using a custom initializer with any required values. This makes it possible to use view controllers with non-optional initialization requirements in storyboards.
On new OS versions that support Segue Actions, that method will be called and the value it returns will be the destinationViewController of the segue object passed to prepare(for:sender:).
Xcode 11 Release Notes
引数は3つで、coder のみが必須になる。coder が必須なのは、storyboard 上での設定項目を ViewController に受け渡す必要があるため。ViewController 上で下記のように設定しておく。
code:FirstViewController.swift
class FirstViewController: UIViewControlelr {
@IBSegueAction
func makeSecondController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> ViewController? {
let prop: SomePropType = /* ... */
return SecondViewController(coder: coder, prop: prop)
}
}
すると、Interface Builder 上で上記で設定した Segue Action に遷移先を向けることができるようになる。
code:SecondViewController.swift
class SecondViewController: UIViewController {
// 👍
let someProp: SomePropType
init?(coder: NSCoder, prop: SomePropType) {
self.someProp = prop
super.init(coder: coder)
}
}
Tips: Custom Segue の実用的な例
下記参照。
Advance Segue 2019年のSegue事情
参考
View Controller Programming Guide for iOS - Using Segues
Advance Segue 2019年のSegue事情