この記事では、SwiftのNavigation Controllerで前の画面へ戻る時に値を持ち帰る方法を紹介します。
以前、こちらの記事で、Navigation Controllerを使った画面遷移の方法や、次の画面へ変数を渡す方法をご紹介しました。
当然、Navigation Controllerで、前の画面へ戻る時に、変数を持ち帰りたい、ということもありますよね。
ところが、前の画面へ変数を持ち帰る場合は、これまでに比べてプログラムを多く書く必要が出てきます。
次の画面へ遷移する場合は、Segueを使った画面遷移でした。
そのため、Segueから必要情報が取得できましたが、前の画面へ戻る場合は、Segueを使った遷移ではありません。
そのため、以下のような実装をする必要があります。
Navigation Controllerは前の画面へ戻る処理が自動で用意されていましたね。
ただ、これでは、前の画面へ戻る、という処理を検知できないので、前の画面へ戻る処理を自分で実装します。
前の画面へ戻る処理を自分で実装したので、前の画面へ戻るという動作を検知できるようになりました。
前の画面へ戻る動作を検知したら、戻る直前に、変数を前の画面へ渡す処理を実装します。
この記事は、こちらの実装方法を、ソースコード、Storyboard上の画面付きで紹介しています。
この記事では、最終的に以下のようなツールを作れます。
【準備】ツールの画面を作成する
まずは準備として、これから作るツールの画面を作成しましょう。
Navigation Controllerで遷移する2画面を作成し、以下のように作っていきましょう。
画面 | 機能 |
---|---|
1画面目 | 「表示」ボタンを押すと、ラベルに2画面目に入力したテキストを表示する 「移動」ボタンを押すと、次画面へ移動する |
2画面目 | 「Back」ボタンを押すと、前画面へ戻る 「テキストフィールド」に入力したテキストを、前画面へ持ち帰る |
なお、各画面の作り方の詳細は、この記事の先頭で紹介している別の記事にて詳しく解説しています。
もし、画面の作り方がよく分からない場合は、是非、そちらの記事もご覧になって下さい。
1画面目を作る
まずは、1画面目を作りましょう。
上の表示記載の機能を実装していきます。
Storyboardに2つのView Controllerを配置し、1画面目にNavigation Controllerを適用しましょう。
テキストを表示するためのラベルと、2つのボタンを配置しましょう。
ボタンには、「表示」と「移動」とタイトルをつけておきます。
「移動」ボタンを引っ張り、移動ボタンをタップした時に2画面目へ遷移するようにしましょう。
表示形式は、「Show」にしておきます。
2画面目を作る
続いて、2画面目も、上に記載の通りに作っていきましょう。
テキスト入力を受け付けるため、テキストフィールドを配置しましょう。
Navigation Controllerで前の画面へ戻る処理を実装する方法
まずは、Navigation Controllerでの画面遷移後、前の画面へ戻る処理を実装していきましょう。
せっかくなので、以下のように、Navigation ControllerのデフォルトのBackボタンと同じような戻る処理を、実装してみましょう。
以下の流れで実装していきます。
- 「Back」ボタンを実装する
- 前の画面へ戻る処理を実装する
- 実際に動かしてみる
- プログラムの処理を考えてみる
「Back」ボタンを実装する
まずはBackボタンを画面上に実装してみましょう。Backボタンは、プログラムから実装します。
2つ目のView Controller用に、SecondViewControllerクラスを作り、以下のように実装します。
ハイライトがついている箇所が、追加したコードです。
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(back(_:)), for: .touchUpInside)
button.setTitle("Back", for: .normal)
button.setImage(UIImage(systemName: "chevron.backward"), for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 16)
navigationItem.leftBarButtonItem = .init(customView: button)
}
}
前の画面へ戻る処理を実装する
Backボタンができたので、続いて、戻る処理を実装してみます。
Backボタンを押した時の処理を実装しましょう。
ハイライトがついている箇所が、追加したコードです。
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(back(_:)), for: .touchUpInside)
button.setTitle("Back", for: .normal)
button.setImage(UIImage(systemName: "chevron.backward"), for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 16)
navigationItem.leftBarButtonItem = .init(customView: button)
}
@objc private func back(_ sender: Any) {
navigationController?.popViewController(animated: true)
}
}
実際に動かしてみる
前の画面へ戻るプログラムの実装が完了しました。
実際に動かしてみましょう。
以下のように、2つ目のView Controllerと、SecondViewControllerクラスを紐づけてあげましょう。
紐づけが完了したら、シミュレータを起動して、実際に動かしてみましょう。
移動ボタンを押した後、次の画面でBackボタンを押すと、元に戻る動作を確認できましたね。
プログラムの処理を考えてみる
実装したプログラムが何をしているのか、考えてみましょう。
まず、この処理では、表示するボタンを生成しています。
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(back(_:)), for: .touchUpInside)
button.setTitle("Back", for: .normal)
button.setImage(UIImage(systemName: "chevron.backward"), for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 16)
上から順に、以下のように処理をしていますね。
- ボタンを生成
- ボタンを押した時に実行するメソッドを指定
- ボタンのタイトルを「Back」に設定
- ボタンの画像を指定
- ボタンのタイトルの文字サイズを指定
なお、ボタンの画像の表示には、SF Symbolという便利なアイコン集を使っています。
こちらはこちらの記事で紹介していますので、興味のある方は、是非ご覧になって下さい。
こちらの処理で、ナビゲーションバーに、Backボタンを表示させていますね。
navigationItem.leftBarButtonItem = .init(customView: button)
こちらは見ての通りで、ナビゲーションバーの左側に、ボタンを配置する、という意味です。
なお、ナビゲーションバーには、マニュアルにも記載の通り、他にも様々は設置パターンがあります。
要素名 | 用途の概要 |
---|---|
rightBarButtonItems | ナビゲーションバーの右側に要素を設置する |
titleView | ナビゲーションバーの中央に要素を設置する |
leftBarButtonItems | ナビゲーションバーの左側に要素を設置する |
こちらの処理で、前の画面へ戻っています。
@objc private func back(_ sender: Any) {
navigationController?.popViewController(animated: true)
}
popViewControllerを実行すると、今表示されているView Controllerが取り外され、その下の、前に表示されていたView Controllerが表示されます。なので、結果として、前の画面へ戻ることができますね。
なお、前の画面に戻る時、今の画面が下にスライドして遷移しましたよね。
これは、animatedがtrueだからです。animatedをfalseにすると、スライドせず、いきなり、前の画面が表示されるようになります。
Navigation Controllerで前の画面へ変数を持ち帰る方法
前の画面へ戻る処理が実装できたので、いよいよ、前の画面へ戻るのと同時に、前の画面へ変数を持ち帰る処理を実装していきましょう。
以下の流れで実装していきます。
- ボタンを押すとテキストをラベルに表示するプログラムを作成する
- 2画面目から1画面目へ変数を持ち帰るプログラムを作成する
- 実際に動かしてみる
- プログラムの処理を考えてみる
ボタンを押すとテキストをラベルに表示するプログラムを作成する
まずは、1画面目で、「表示」ボタンを押すと、テキストをラベルに表示するプログラムを作ってみましょう。
1つ目のView Controller用のプログラムに、以下の通りに実装します。
ハイライトがついている箇所が、追加したコードです。
import UIKit
class ViewController: UIViewController {
// テキストを表示するラベル
@IBOutlet var label: UILabel!
// 次の画面で入力されたテキスト
var text:String = "Label"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func tapButton(sender: UIButton) {
label.text = text
}
}
2画面目から1画面目へ変数を持ち帰るプログラムを作成する
続いて、2画面目から、1画面目へ変数を持ち帰るプログラムを実装しましょう。
2画面目のテキストフィールドに入力されたテキストを、1画面目の変数「text」へ代入できれば良さそうですね。
2画面目用のSecondViewControllerクラスへ、プログラムを追加していきます。
ハイライトがついている箇所が、追加したコードです。
import UIKit
class SecondViewController: UIViewController {
// テキストフィールド
@IBOutlet var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(back(_:)), for: .touchUpInside)
button.setTitle("Back", for: .normal)
button.setImage(UIImage(systemName: "chevron.backward"), for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 16)
navigationItem.leftBarButtonItem = .init(customView: button)
}
@objc private func back(_ sender: Any) {
let nc = self.navigationController!
let vc = nc.viewControllers[nc.viewControllers.count - 2] as! ViewController
vc.text = textField.text!
navigationController?.popViewController(animated: true)
}
}
実際に動かしてみる
とうとうプログラムが全て完成しました。
実際に動かしてみる前に、まずは、Storyboard上でプログラムと各要素を紐づけてましょう。
1つ目のView Controller上のLabelとButtonを、プログラムと紐づけます。
2つ目のView Controller上のTextFiledも、プログラムと紐づけましょう。
これで準備は完了です。実際に、シミュレータで動かしてみましょう。
2画面目でテキストを入力した後、1画面目へ戻り、表示ボタンをタップすると、入力したテキストが表示されますね。これで、ツールは完成です!
プログラムの仕組みを考えてみる
実装したプログラムが何をしているのか、考えてみましょう。
今回ポイントとなるのは、こちらの処理ですね。こちらで、前のViewControllerを取得して、変数を代入しています。
let nc = self.navigationController!
let vc = nc.viewControllers[nc.viewControllers.count - 2] as! ViewController
vc.text = textField.text!
特にポイントとなるのが、2行目の処理です。
1行目で取得したUINavigationControllerクラスから、viewControllersプロパティを取得して、操作しています。viewControllersとはなんでしょうか。マニュアルを見てみましょう。
The view controllers currently on the navigation stack.
つまり、viewControllersには、これまで遷移してきた各画面の情報が入っているようです。
The root view controller is at index
0
in the array, the back view controller is at indexn-2
, and the top controller is at indexn-1
, wheren
is the number of items in the array.
1つ前のView Controllerへ戻る場合には、viewControllers配列の、2つ前のView Controllerへ遷移すれば良いようですね。また、例えば、1つ前には、先頭のView Controllerが入っているようです。
これで、プログラムの処理が分かりましたね。
マニュアルに従って、プログラムの2行目で、2つ前のView Controller情報 ( = 1つ前のページの情報 ) を取得し、そのView Controllerへ、変数を渡していたわけです。
また、この処理を応用すれば、トップページや、2つ、3つ前のページへも、変数を渡すプログラムも作れそうですね!