まさかの癌宣告で前回から1か月以上あいちゃったけど、予告通りストーリーボードの話。
ちなみに抗がん剤治療の副作用で、冷たいものに触ると指先がビリビリします。なので部屋を暖かくしてからMacBook Proのトラックパッドを触るようにしてる。
ストーリーボード
ストーリーボードってなんだというと、「スクリプトエンジンっぽい」で作った辞書を使った脚本オブジェクトみたいなもんです。
あの時は、アプリで表示させるビューやその動作を辞書に脚本として記述して、それを読み出して実行するプログラムを作ったわけですが、ストーリーボードも同じように、画面上にどんなビューを出したり、タップを通知したりということを記述できたりします。
で、こいつはSwiftのプログラムコードのように、テキストエディタを使って記述するのではなく、Interface BuilderというXcodeに組み込まれた専用のエディタを使って記述するようになってます。
でもって、その記述したものをファイルとして保存して任意のタイミングで読み込んで利用する。
.storyboardファイル
それが.storyboardって拡張子のファイルなんだけど、実はXcodeのiPhoneアプリ用プロジェクトのテンプレートは全て、この.storyboardファイルを利用するようになってます。
それがこのMain.storyboardってファイル。これまで作ったプロジェクトにも入ってて、ナビゲーションエリアのMain.storyboardを選ぶと表示されます。
サンプル:
http://tetera.jp/xcc/book-sample/storyboard.zip
エディタエリアに表示されてる画面はInterface Builderのもので、左の領域をoutlineビュー、右の領域をcanvasビューと呼びます。
outlineでストーリーボードに記述されているオブジェクトの一覧を階層構造で見せ、canvasで、それらのオブジェクトが実際のiPhoneの画面をどう表示するかを見せてるわけですよ。
でもって、どっちのビューでもオブジェクト単位で選択ができるようになってて、互いに連動してハイライトされるようになってもいます。
で、そのまた右のユーティリティエリアでは、選択されたオブジェクトの情報が表示されるという具合。
んじゃ、そのMain.storyboard様には何が記述されてるかというと、ViewControllerオブジェクトの作成なんですな。
そういうわけで、今回のプロジェクトのMain.storyboardを選んだエディタエリアには、ViewControllerオブジェクトと、そいつが持つUIView(self.view)が表示されていることになります。
本当にそうなのか疑う人はoutlineでView Controller項目を選んで、ユーティリティエリアでIdentityインスペクタ画面を表示させてみましょう。
classのところがViewControllerってなってることが確認できるでしょう。
以前「こんにちは世界」で触れ、これまで説明していなかった
このアプリは、起動するとViewControllerオブジェクトが1つ用意されるようになってるんですよ
に対して当然湧き上がるであろう疑問、「じゃあ、なんで、アプリは起動するとViewControllerオブジェクトを用意するのか?」の仕組みがこれです。
答え:Main.storyboardでViewControllerを作れって指定されてるから。
でもって、プロジェクトでアプリ起動時にMain.storyboard読み込んで使えって指定してるから。
ちなみに、どこでMain.storyboardが利用されるように指定されてるかというと
ここ↓
ここに指定された名前と拡張子.storyboardを組み合わせたファイルが、アプリ起動時に読み込まれて利用されるようになっている。
今回ならMain.storyboardファイルがアプリ起動時に読み込まれて利用されるわけですよ。
なので、例えば、このMain.storyboardでViewControllerじゃなく地図画面用であるMapViewControllerを作るように記述すれば、アプリ起動時に地図画面を表示するようにだってできます。
そこらへんは追い追いやるとして、とりあえずは手馴しとしてViewController.swiftのviewDidLoadメソッドで記述した、タップの見張りと通知なんかをMain.storyboardで記述してみることにしましょう。
↓こいつね。
class ViewController: UIViewController, ・・・ override func viewDidLoad() { super.viewDidLoad() self.view.addGestureRecognizer(UITapGestureRecognizer( target: self, action: #selector(showAsView))) }
UITapGestureRecognizerの作成とself.viewへの登録、そしてtargetとactionの設定をストーリーボードで記述するわけです。
まずはUITapGestureRecognizerの作成とself.viewへの登録から。
どうやるかというと、ユーティリティエリア下にあるライブラリエリアに表示されてるTap Gesture Recognizerという項目を、エディタエリアのcanvasビューのViewにドラッグ&ドロップしてやります。
1、ライブラリエリアでObjectライブラリを選択してオブジェクト群を表示させる。
2、表示された項目からTap Gesture Recognizer項目を探し、これをcanvasビュー上のViewにドラッグ&ドロップする。
Viewはoutlineの階層を見ると察しがつくと思うけど、ViewControllerの持ってるUIView、つまりself.viewを意味してます。これで
self.view.addGestureRecognizer(UITapGestureRecognizer()))
と同じことを記述したことになるんですな。
ちなみに、objectライブラリのTap Gesture Recognizer項をダブルクリックすると、どういうクラスのオブジェクトかわかるようになってます。
で、これでUITapGestureRecognizer作ってself.viewに登録するまではできたわけですが、これだけだとtarget: self, action: #selector(showAsView)部分が記述できてないわけですよ。
これをどうするか?
↓これね。
self.view.addGestureRecognizer(UITapGestureRecognizer(
target: self, action: #selector(showAsView)))
まず必要なのは、ViewController.swiftにテキストエディタを使って記述したshowAsViewメソッドを、ストーリーボードを記述するInterface Builderに認識させること。
@IBAction
そのために一度、ナビゲーションエリアでViewController.swiftを選び、テキストエディタでViewController.swiftのshowAsViewメソッドの前についてる@objcを@IBActionに書き換えます。
class ViewController: UIViewController, ・・・ @objc func showAsView() {
↓
class ViewController: UIViewController, ・・・ @IBAction func showAsView() {
この@IBActionこそが、Interface Builderに対して、このメソッドはターゲットアクションデザインパターンのアクションとして指定可能なメソッドだよって知らせるキーワードなわけですな。ちなみに@IBActionのついたメソッドは@objc指定であることも保証します。
ターゲットアクションデザインパターンや@objcが何かわからん人は「タップしてドン」と前回の「そして委譲へ」を読みましょう。
書き直したら再びナビゲーションエリアのMain.storyboardを選び、outlineビューのView Controller項目を選択し、ユーティリティエリアのConnectionsインスペクタを表示させてみる。
そうすると下の方のRecieved ActionsのグループにshowAsViewの項目が出現するんですな。これがInterface BuilderがshowAsViewメソッドをターゲットアクションデザインパターンのアクションとして指定可能なメソッドとして認識した証拠。
で、これが出ると、このshowAsViewの項目の右にある◎からドラッグで線を引き出して、outlineビューのTap Gesture Recognizer項目に繋ぐことができるようになります。
でもって、上の図のようになったところでマウスのボタンを放すと、これでUITapGestureRecognizerオブジェクトのtargetとしてViewControllerが、actionとしてshowAsViewメソッドが設定されることになるわけです。
つまり、これで
self.view.addGestureRecognizer(UITapGestureRecognizer( target: self, action: #selector(showAsView)))
と同じことが、ストーリーボードに記述できたことになるんですな。
なので、ViewController.swiftのviewDidLoadメソッドで記述したUITapGestureRecognizerオブジェクトの作成とself.viewへの登録は不要になりました。そのままだとストーリーボード、viewDidLoadメソッドの両方でUITapGestureRecognizerオブジェクトを作っちゃってself.viewへ多重登録することになるんで、viewDidLoadメソッド側は消しちゃってください。
↓こいつね。
class ViewController: UIViewController, ・・・ override func viewDidLoad() { super.viewDidLoad()self.view.addGestureRecognizer(UITapGestureRecognizer( target: self, action: #selector(showAsView)))}
サンプル:
http://tetera.jp/xcc/book-sample/storyboard-2.zip
消したらRun!
viewDidLoadメソッドの処理が無いのに、ちゃんと画面タップで地図画面が表示されるはず。
グレート。
というか、ぶっちゃけ、これくらいならviewDidLoadメソッドにテキストエディタでプログラムコード直接記述する方がよっぽど早いんすけどね。
違うから!
本来、ストーリーボードを使うメリットは、View上にボタンといったサブビューをAuto Layout使って配置する時に出てくるんだから。
というわけで、次回は、ストーリーボード上でボタンを配置してタップしたら地図画面が表示されるようにしてみる。