【Swift】Split View Controllerの使い方。ボタンでマスター部を出し入れする方法。(Swift 2.1、XCode 7.2)
2020年6月16日
ボタンでマスター部を出し入れする
前回の記事で、Split View Controller(以下、スプリットビューコントロラー)を使って画面をマスター部とディテール部に分割する方法を説明した。⇒「前回記事」
その記事の実装では、マスター部を引き出すときは画面端から内側に向かってドラッグした。
画面端からドラッグはやりづらいので、ドラッグ以外の方法で出し入れしたいと思うことがある。そこで本記事では、ボタンを押してマスター部を出し入れする実装方法を説明する。
具体的には、ナビゲーションバーの左端にボタンを表示し、ボタンを押してマスター部を出し入れできるようにする。
以降の作業は前回記事の実装結果から行う。前回記事の実装結果のXcodeプロジェクトをGitHubに置いたので、試してみる人はご利用されたし。
⇒「テスト用プロジェクト」
前回記事の実装では、下図のようにディテール部はナビゲーションコントローラーを介さずにビューコントローラーをそのまま起動した。
上図のままではiPadで起動したときにディテール部にナビゲーションバーが存在しないので、下図のようにディテール部もナビゲーションコントロールを介して起動するようにこれから変更する。
ストーリーボードのビューコントローラーを選択したあとに、メニューの「Editor」⇒「Embed In」⇒「Navigation Controller」を選択する。すると、ビューコントローラーと接続したナビゲーションコントローラーが作られる。
TestTableViewController.swiftを以下のコードに変更する。
viewDidLoadメソッドで、ナビゲーションバーの左ボタンに画面モード切り替えボタンを配置している。
切り替えボタンはiPhoneなどのCompact widthの端末では機能しない。そのため、戻るボタンを切り替えボタンに置き換えてしまうと、iPhoneで起動したときにナビゲーションバーに何も表示されなくなる。そこで、leftItemsSupplementBackButtonプロパティをtrueにして、戻るボタンの後ろに切り替えボタンが追加されるようにした。
「画面遷移実行前の呼び出しメソッド」でキャストするクラスをナビゲーションコントローラーに変更し、ナビゲーションコントローラーからビューコントローラーを取得して、画像ファイル名を設定するようにした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// // TestTableViewController.swift // import UIKit class TestTableViewController: UITableViewController { private let tableData = ["test_morning_sample", "test_evening_sample", "test_night_sample"] //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() //ナビゲーションバーの左ボタンに画面モードの切り替えボタンを表示する。 navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem() //戻るボタンの後ろに表示する。 navigationItem.leftItemsSupplementBackButton = true } //データの個数を返すメソッド override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tableData.count } //データを返すメソッド override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { //セルを取得し、テキストを設定して返す。 let cell = tableView.dequeueReusableCellWithIdentifier("TestCell") as UITableViewCell! cell.textLabel?.text = tableData[indexPath.row] return cell } //画面遷移実行前の呼び出しメソッド override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { //遷移先のナビゲーションコントローラーからビューコントローラーを取得し、画像を設定する。 if let navigationController = segue.destinationViewController as? UINavigationController { if let selectedRowIndexPath = tableView.indexPathForSelectedRow { let controller = navigationController.viewControllers.first as! ViewController controller.imageName = tableData[selectedRowIndexPath.row] } } } } |
ViewController.swiftを以下のコードに変更する。
先ほどと同じように、viewDidLoadメソッドでナビゲーションバーの左ボタンに画面モード切り替えボタンを設定している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// // ViewController.swift // import UIKit class ViewController: UIViewController { @IBOutlet weak var testImageView: UIImageView! var imageName:String! //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() //画像を設定する。 if(imageName != nil) { testImageView.image = UIImage(named: imageName) } //戻るボタンの後ろに表示する。 navigationItem.leftItemsSupplementBackButton = true //ナビゲーションバーの左ボタンに画面モードの切り替えボタンを表示する。 navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem() } } |
以下はiPadのプレイ動画。ナビゲーションバーの左ボタンを押してマスター部を出し入れできるようになった。
以下はiPhoneのプレイ動画。こちらは変更前と何も変わらない。
AppDelegateで切り替えボタンを設置する
上記コードは、各クラスでナビゲーションバーの切り替えボタンを設定したが、複数画面で共通して使われるナビゲーションバーの設定を各クラスで設定し直すのはスマートではない。通常、このような設定はAppDelegateで一括して設定する。
しかし、「Show Detail」のセグエを使って画面遷移させると、ディテール部の画面が置換されるため起動時に設定したナビゲーションバーのアイテムが消えてしまう。そこで、セグエを使わずにディテール部を変更することにした。
(※もっとスマートな方法が見つかった場合はわかり次第追記する。)
セルとナビゲーションコントローラーを接続したセグエ(下図赤枠)を選択し、Deleteキーで削除する。
AppDelegate.swiftを以下のコードに変更する。
アプリ起動時に、マスター部とディテール部のナビゲーションバーに切り替えボタンを追加している。
マスター部のデリゲート先にディテール部のビューコントローラーを設定し、セルがタップされたときのイベントをマスター部からディテール部に渡せるようにした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
// // AppDelegate.swift // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? //アプリ起動時の呼び出しメソッド func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //スプリットビューコントローラーを取得する。 let splitViewController = self.window!.rootViewController as! UISplitViewController //マスター部のテーブルビューコントローラーを取得する。 let masterNavController = splitViewController.viewControllers.first as! UINavigationController let masterViewController = masterNavController.topViewController as! TestTableViewController //新しいボタンは戻るボタンの横に追加されるように設定する。 masterViewController.navigationItem.leftItemsSupplementBackButton = true //マスター部のナビゲーションバーの左ボタンに画面モードの切り替えボタンを追加する。 masterViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem() //ディテール部のビューコントローラーを取得する。 let detailNavController = splitViewController.viewControllers.last as! UINavigationController let detailViewController = detailNavController.topViewController as! ViewController //新しいボタンは戻るボタンの横に追加されるように設定する。 detailViewController.navigationItem.leftItemsSupplementBackButton = true //ディテール部のナビゲーションバーの左ボタンに画面モードの切り替えボタンを設定する。 detailViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem() //マスター部のデリゲート先にディテール部のビューコントローラーを設定する。 masterViewController.delegate = detailViewController return true } func applicationWillResignActive(application: UIApplication) { } func applicationDidEnterBackground(application: UIApplication) { } func applicationWillEnterForeground(application: UIApplication) { } func applicationDidBecomeActive(application: UIApplication) { } func applicationWillTerminate(application: UIApplication) { } } |
TestTableViewController.swiftを以下のコードに変更する。
ViewControllerに適用するプロトコル(TestTableViewControllerDelegate)を新しく作成した。
「データ選択時の呼び出しメソッド」で、ディテール部のデリゲートメソッドを呼び出したあとにディテール部の画面表示に切り替えている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
// // TestTableViewController.swift // import UIKit //デリゲート protocol TestTableViewControllerDelegate: class { func selectedCell(image: UIImage) } class TestTableViewController: UITableViewController { private let tableData = ["test_morning_sample", "test_evening_sample", "test_night_sample"] var delegate: TestTableViewControllerDelegate? //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() } //データの個数を返すメソッド override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tableData.count } //データを返すメソッド override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { //セルを取得し、テキストを設定して返す。 let cell = tableView.dequeueReusableCellWithIdentifier("TestCell") as UITableViewCell! cell.textLabel?.text = tableData[indexPath.row] return cell } //データ選択後の呼び出しメソッド override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { if let selectedRowIndexPath = tableView.indexPathForSelectedRow { //デリゲートメソッドを呼び出す。 self.delegate?.selectedCell(UIImage(named:tableData[selectedRowIndexPath.row])!) } if let detailViewController = self.delegate as? ViewController { //ディテール部を表示する。 splitViewController?.showDetailViewController(detailViewController.navigationController!, sender: nil) } } } |
ViewController.swiftを以下のコードに変更する。
上記で作成したプロトコルを適用し、デリゲートメソッドの「セル選択時の呼び出しメソッド」でイメージビューに画像を設定している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// // ViewController.swift // import UIKit class ViewController: UIViewController, TestTableViewControllerDelegate { @IBOutlet weak var testImageView: UIImageView! var imageName:String! //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() //画像を設定する。 if(imageName != nil) { testImageView.image = UIImage(named: imageName) } } //セル選択時の呼び出しメソッド func selectedCell(image: UIImage) { testImageView.image = image } } |
以下はiPadのプレイ動画。前回の実装と動きは同じ。
以下はiPhoneのプレイ動画。前回の実装と動きは同じ。
自分好みのボタンに変える。
マスター部を出し入れするボタンを自分好みの画像や文字列にするとき、そのまま設定すると以下の動画のようにiPhoneで起動したときに「戻るボタン」の横に切り替えボタンが表示され、しかもiPhoneでは切り替えボタンは機能しないので、何もできないボタンが表示されるだけになる。
なので、画面モードの状態からCompact width または Regular widthのどちらでで起動されたのか判別し、Regular widthで起動されているときのみボタンを追加する作りに変更する。
AppDelegate.swiftを以下のコードに変更する。その他のファイルは変更無し。
画面モードがPrimary Hiddenの場合にRegular widthの端末で起動されたと判断し、アイコンを設定したボタンを左ボタンに登録している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
// // AppDelegate.swift // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? //アプリ起動時の呼び出しメソッド func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //スプリットビューコントローラーを取得する。 let splitViewController = self.window!.rootViewController as! UISplitViewController //マスター部のテーブルビューコントローラーを取得する。 let masterNavController = splitViewController.viewControllers.first as! UINavigationController let masterViewController = masterNavController.topViewController as! TestTableViewController //ディテール部のビューコントローラーを取得する。 let detailNavController = splitViewController.viewControllers.last as! UINavigationController let detailViewController = detailNavController.topViewController as! ViewController //起動時の画面モードからボタンを追加するかどうかを決める。 if(splitViewController.displayMode == UISplitViewControllerDisplayMode.PrimaryHidden) { //新しいボタンは戻るボタンの横に追加されるように設定する。 masterViewController.navigationItem.leftItemsSupplementBackButton = true //ボタンを作成する。 let closeButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Rewind, target: splitViewController.displayModeButtonItem().target, action: splitViewController.displayModeButtonItem().action) //マスター部のナビゲーションバーの左ボタンに画面モードの切り替えボタンを追加する。 masterViewController.navigationItem.leftBarButtonItem = closeButton //新しいボタンは戻るボタンの横に追加されるように設定する。 detailViewController.navigationItem.leftItemsSupplementBackButton = true //ボタンを作成する。 let openButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FastForward, target: splitViewController.displayModeButtonItem().target, action: splitViewController.displayModeButtonItem().action) //ディテール部のナビゲーションバーの左ボタンに画面モードの切り替えボタンを設定する。 detailViewController.navigationItem.leftBarButtonItem = openButton } //マスター部のデリゲート先にディテール部のビューコントローラーを設定する。 masterViewController.delegate = detailViewController return true } func applicationWillResignActive(application: UIApplication) { } func applicationDidEnterBackground(application: UIApplication) { } func applicationWillEnterForeground(application: UIApplication) { } func applicationDidBecomeActive(application: UIApplication) { } func applicationWillTerminate(application: UIApplication) { } } |
以下はiPadのプレイ動画。システムアイコンのボタンが表示された。
以下はiPhoneのプレイ動画。システムアイコンは表示されず、いつもの戻るボタンが表示された。