はじめに
画面遷移で Segue はもうほとんど使ってないというのを聞いたので Segue を使う利点を捻り出してみました
(私は特に Segue 推奨派というわけではありません)
そもそも Storyboard 使わない!という方法もあると思いますが今回はそこにはふれません。
つくるもの
Root -> NavigationController -> First -> Second -> Third
上記のように遷移して値を渡して Third から Root に戻るときにも値を渡す方法を考えてみます。
こんな感じ(遷移時に first とか second の文字列を渡してラベルに表示しています)
Segueを使う場合
Segue を使う場合 storyboard はこんな感じです。
コードは Root がこんな感じになります。
final class RootViewController: UIViewController {
@IBOutlet private weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = ""
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let nav = segue.destination as? UINavigationController,
let first = nav.viewControllers.first as? FirstViewController,
let text = sender as? String {
first.text = text
}
}
override func canPerformUnwindSegueAction(_ action: Selector, from fromViewController: UIViewController, sender: Any?) -> Bool {
if fromViewController is ThirdViewController {
if let text = sender as? String {
label.text = text
}
}
return true
}
@IBAction private func showFirst(_ sender: Any) {
performSegue(withIdentifier: "showFirst", sender: "root")
}
// Unwind Segue
@IBAction private func backFromThird(segue: UIStoryboardSegue) {
}
}
Third がこんな感じになります。
final class ThirdViewController: UIViewController {
var text: String?
@IBOutlet private weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = text
}
@IBAction private func backToRoot(_ sender: Any) {
performSegue(withIdentifier: "backToRoot", sender: (text ?? "") + ", third")
}
}
その他のコード
final class FirstViewController: UIViewController {
var text: String?
@IBOutlet private weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = text
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let second = segue.destination as? SecondViewController,
let text = sender as? String {
second.text = text
}
}
@IBAction private func showSecond(_ sender: Any) {
performSegue(withIdentifier: "showSecond", sender: (text ?? "") + ", first")
}
}
final class SecondViewController: UIViewController {
var text: String?
@IBOutlet private weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = text
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let third = segue.destination as? ThirdViewController,
let text = sender as? String {
third.text = text
}
}
@IBAction private func showThird(_ sender: Any) {
performSegue(withIdentifier: "showThird", sender: (text ?? "") + ", second")
}
}
Segueを使わない場合
Segue を使わない場合 storyboard はこんな感じです。
コードは Root がこんな感じになります。
final class RootViewController: UIViewController {
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = ""
}
@IBAction private func showFirst(_ sender: Any) {
if let nav = storyboard?.instantiateViewController(withIdentifier: "FirstNavigation") as? UINavigationController,
let first = nav.viewControllers.first as? FirstViewController {
first.text = "root"
nav.modalPresentationStyle = .fullScreen
present(nav, animated: true)
}
}
}
Third がこんな感じになります。
final class ThirdViewController: UIViewController {
var text: String?
@IBOutlet private weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = text
}
@IBAction private func backToRoot(_ sender: Any) {
if let root = navigationController?.presentingViewController as? RootViewController {
root.label.text = (text ?? "") + ", third"
root.dismiss(animated: true, completion: nil)
}
}
}
その他のコード
final class FirstViewController: UIViewController {
var text: String?
@IBOutlet private weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = text
}
@IBAction private func showSecond(_ sender: Any) {
if let second = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController {
second.text = (text ?? "") + ", first"
navigationController?.pushViewController(second, animated: true)
}
}
}
final class SecondViewController: UIViewController {
var text: String?
@IBOutlet private weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = text
}
@IBAction private func showThird(_ sender: Any) {
if let third = storyboard?.instantiateViewController(withIdentifier: "ThirdViewController") as? ThirdViewController {
third.text = (text ?? "") + ", second"
navigationController?.pushViewController(third, animated: true)
}
}
}
比較
2つを比較して Segue のメリットを捻出してみます
-
Unwind Segue が使える
Unwind Segue を使えば Third から Root に戻るときは Modal か Push か気にする必要がなくなります。 -
Third から Root に値を渡しやすい
Segue を利用しない場合、下記のように Root に値を渡すときに Modal か Push かによって処理が大きく変わります。Segue を利用すれば遷移方法に依存せず値を渡すことができます!@IBAction private func backToRoot(_ sender: Any) { if let root = navigationController?.presentingViewController as? RootViewController { root.label.text = (text ?? "") + ", third" root.dismiss(animated: true, completion: nil) } }
おわりに
がんばって Segue の利点について書こうと思いましたが1画面からいろいろな画面に遷移する場合はどうしても prepareForSegue
が太ってくるのでそこは難点
id のタイポ問題とかは R.swift を使えば解決すると思います。
なんか他に利点ないかな