「Start Developing iOS Apps (Swift)」でFoodTracker Appを実際に作ってみる その3 [UIとコードを繋ぐ]

Start Developing iOS AppでFoodTrackerを実際に作ってみる、その3

公式にある「Start Developing iOS Apps (Swift)」を使って実際にFoodTracker Appを作ってみる、というシリーズの第三回目です。前回はこちら

「Start Developing iOS Apps (Swift)」でFoodTracker Appを実際に作ってみる その2 [基本的なUIを作る]
タイトルにもあるように、公式にある「Start Developing iOS Apps (Swift)」を使って実際にFoodTracker...

前回はXcodeを使って基本的なUIを作りました。今回は作ったUIに具体的なアクションを実装する、という回です。

この記事を書いている時点でのXcodeのバージョンは7.3です。

追記: Swift 3がリリースされたので、Swift 3/Xcode 8.0の仕様に合わせました。バージョンによる違い、警告やエラー等に関してはXcode 7.3の記述をそのまま載せる場合がありますが、その際は補足します。

目次

ユーザーインターフェースとコードを繋げる | Connect the UI to Code

「Learning Objectives(学習目的)」です。第三回目が終了した時点で、以下の項目が出来るようになる(ことが目標)。主語は私達です。

  • ストーリーボード上のsceneとview controllerの関係を説明出来る
  • アウトレット(outlet)やアクション(action)を作り、ストーリーボード上のUI要素とソースコードを繋ぐことが出来る
  • テキストフィールドからのユーザによる入力を処理して、結果をUIに表示出来る
  • プロトコル(Protocol)に準拠したクラスを作る
  • Delegationパターンを理解する
  • アプリの仕様を設計する際、target-actionパターンに従う

Part3が終わった時点での(シミュレータ上での)見た目は
Xcode: シミュレータでテキストフィールドに設定したアクションのテスト
こんな感じになる予定です。

UIとソースコードを繋げる | Connect the UI to Source Code

Sceneとview controllerの関係

Sceneとは?

Sceneというのは(前回出て来ましたが)、

アプリの1画面をストーリーボード上で見た時の表示

で、普通は1つのview controllerを含んでいるようです。

View controllerとは?

前回もViewController.swiftというファイルが出て来てさらりと触れましたが、view controllerの役割は、

  • 複数のviewsの管理(subviewsも含む)
  • カプセル化されたデータとviews間の情報の流れを調整する

等々、多岐に渡ります。ここで「カプセル化されたデータ」というのはapp’s data modelという部分で、data modelというのは後で自分たちが定義するクラスのことです。上記以外の役割も色々あって、

View controllers implement your app’s behavior. A view controller manages a single content view with its hierarchy of subviews. View controllers coordinate the flow of information between the app’s data model, which encapsulates the app’s data, and the views that display that data, manage the life cycle of their content views, handle orientation changes when the device is rotated, define the navigation within your app, and implement the behavior to respond to user input.

例えば、iOSデバイス自体が回転した時に画面の向きも回転させる、という操作をやってくれるのもview controllerということになります。

View controllerはUIViewControllerのサブクラス

View controllerサブクラスを使って機能を実装

タイトルのままですが、

iOSのview controllerオブジェクトは、全てUIViewControllerまたはそのサブクラス、

になります。実際UIViewControllerそのままを実装するというケースはあるのか分かりませんが、基本的にはUIViewControllerのサブクラスを作ってその振る舞いをカスタマイズすることになります。

Xcodeは自動的にview controllerサブクラスを用意

ViewController.swiftは前回出て来ましたが、Xcodeは自動的にViewControllerサブクラスを作っていることが分かります。さらに、そのViewControllerサブクラスは、今触っているストーリーボード上のsceneと繋がっているということも分かります。開発者(我々)は特に何もしなくても、Xcodeが必要なモノを自動的に色々作ってくれるのは便利ですね。

まだsceneは1つですが、今後複数のsceneを追加していった場合、「どのview controllerがどのsceneに対応しているのか」、という関連付けをidentity inspectorから行うようです。Identity inspectorは(Xcodeの右側にある)utility area内のメニューですが、実際設定する時に詳しく見ることにして、ここでは説明を割愛します。

アウトレット(outlets)とアクション(actions)でストーリーボードとコードを繋ぐ

View controllerのソースコードファイルとストーリーボードのviews間を繋げる仕組みが、次に見ていくアウトレット(outlets)とアクション(actions)です。この2つを理解すると、具体的にview controllerのコードとストーリーボード上のオブジェクトがどういう風に繋がっているのかが理解できると思います。

先ずアウトレットをViewController.swiftに追加する方法を詳しく説明します。

UI要素用アウトレットを作る | Create Outlets for UI Elements

アウトレット(Outlets)って何?

Xcode上でのアウトレット(Outlets)とは

ストーリーボード内オブジェクトに対する、ソースコードファイルからの参照

です。

ここでは1つ前の回に作ったテキストフィールドとラベルに対して、view controllerファイルに参照用のプロパティを作っていきます。分かりやすいように対応関係を書くと、

  • テキストフィールド(またはラベル) = ストーリーボード内オブジェクト
  • ソースコードファイル = view controllerファイル
  • 参照用のプロパティ = view controllerファイルに書いてある(これから書く)参照

という感じです。アプリ実行時には、このプロパティを通してストーリーボード上に配置したオブジェクトにアクセス・操作します。

以下では、「アウトレットを作るにはどうすれば良いか?」という点を詳しく説明します。やり方を強引に一言でまとめると、

ストーリーボード上のオブジェクトを選択してview controller上にcontrol+ドラッグする

です。

まずはテキストフィールド用のアウトレットを作る手順を説明します。少し回りくどいですが、1つ1つの操作を順番に書いていきます。

Main.storyboardを開く

すでに開いているかもしれませんが、もし開いていない場合はnavigator area(Xcodeの一番左側にある縦長メニュー)からMain.storyboardを選択。

Assistant editorボタンをクリック

Assistant editorは前回も使いました。

Xcode: Assistant editorのボタン
Toolbarの右側にある丸が2つ重なったようなボタンがassistant editor用ボタンです。非表示だと灰色なので、クリックしてアクティブ(青色)にします。

スペース確保のためnavigator areaとutility areaを隠す

Assistant editorを開くと画面が狭くなるので、作業用スペースを広く確保したい場合はnavigator area(project navigatorとも呼んでいますね)とutility areaを隠します。

Xcode: Toolbarのボタン
Workspace windowのメニュー表示・非表示は、toolbar一番右にあるボタン3つで行います。左がnavigator area、右がutility areaに対応しています。真ん中は使ってませんが、editor areaの下側にdebug areaというデバッグ情報表示窓を出すボタンです。Outline viewも隠すと、さらに広く使えます。

ViewController.swiftをassistant editorで開く

ソースコードViewController.swiftの中身をassistant editorで開きます。前回は実機のプレビュー画面にしていたはずなので、Assistant editorの上側にあるメニューでは「Preview」と表示されているはずです。ここをクリックして、

Automatic > ViewController.swift

を選択。ViewController.swiftを開いた後の画面が、

Xcode: Assistant editorでViewController.swiftを開いた状態
このようになるはずです。

クラス宣言の記述(class ViewController: …)を見つける

class ViewController: UIViewController {

と書いてある部分を見つけます。これを見ると確かにViewControllerUIViewControllerのサブクラスであることが分かります。

アウトレットを追加する場所にコメントを書く

先程のクラス宣言以下に、

// MARK: Properties

というコメントを追加します。

コメントはコンパイル時に無視されますが、人間(自分自身や共同開発者)に分かりやすい情報を提供するためのツールです。ここで出て来た

// MARK:

という文字列は特殊なコメントの1つで、コードを管理するのに非常に便利な機能になっています。上記のコメントは、

このコメントより下にはプロパティが列挙されてます

ということをXcodeに伝える特別な識別子のようなものです。便利機能に関しては後で説明するので、ここでは詳細については割愛します。

テキストフィールドを選択「control+ドラッグ」でアウトレットを作成

ストーリーボード上のテキストフィールドをクリックして選択します。選択したらcontrolを押しながら、ストーリーボード上のcanvasから右側のViewController.swift上にテキストフィールドをドラッグします。

Xcode: control+ドラッグでアウトレットを作る
(画像をクリックすると拡大します)
controlを押しながらドラッグして、先程書いた// MARK: Propertiesの下に落とします。少し見づらいですが上の画像を参照。

名前を付ける: nameTextField

ドラッグしてViewController.swift上で落とすと、

Xcode: Outletに名前を付ける
上の画像で示したようなダイアログボックスが出て来ます。今は名前をnameTextFieldに設定、それ以外のオプションはデフォルトのままにしておきます。

ダイアログボックスの「Connect」をクリック

ダイアログボックス右下にある「Connect」をクリックすると、ViewController.swiftに

@IBOutlet weak var nameTextField: UITextField!

が追加されるはずです。ぱっと見複雑過ぎて心が折れそうですが、分解するとそうでもないので順番に見ていきます。

IBOutlet attributeとweak参照

まず@IBOutletですが、一つ前の回に出て来た@UIApplicationMainと同じ表記で、Swiftでは「attribute」と呼んでいる機能です。この修飾子はXcodeに、

nameTextFieldプロパティをinterface builderで使う

と教えるためのモノです。Interface Builderなので「IB」という接頭辞が付いているようです。イメージとしては、このプロパティとストーリーボード上(Interface Builder上)のUIが繋がっている感じです。実際ドラッグした際にも、ストーリーボード上からコードまで線が出るので、「繋がっている」という感覚はあると思います。

weak修飾子は、参照が「弱参照」であるという宣言です。

公式だとここで「弱参照というのは、プロパティがどこかの時点でnilになる可能性がある」云々と説明しています。しかし、weakにする理由は循環参照回避のためだと思いますが、どのクラス間での循環参照を避けたいのかが未だ分かってません。

変数宣言: var nameTextField: UITextField!

残りは普通の変数宣言で、

UITextFieldという型のnameTextFieldという変数を宣言する

ということになります。

また、最後にビックリマーク!が付いているので、これはimplicitly unwrapped optionalsというオプショナル型です(オプショナル型だけど、最初に値がセットされた後はnilにならない)。

これでテキストフィールドのアウトレットが(ひとまず)完成しましたので、次は同様にラベルのアウトレットを作ります。

ラベルのアウトレットを作成

手順は全く同じなので箇条書きにすると、

  • ストーリーボードでラベルを選択
  • control+ドラッグでcanvasからassistant editorに引っ張り、nameTextField直下に落とす
  • ダイアログボックスで名前を「mealNameLabel」と付ける
  • ダイアログボックス右下の「Connect」をクリック

追加したアウトレットは

@IBOutlet weak var mealNameLabel: UILabel!

となっているはずです。テキストフィールドと違うのは型(UILabel)ですね。

アウトレットを作れるようになったので、これでアプリのインターフェースをソースコードから参照することが出来ます。次は「アクション(actions)」を作ります。

実行するアクションを定義する | Define an Action to Perform

イベント駆動型プログラミング | event-driven programming

イベントって何?

iOSアプリは「イベント駆動型プログラミング(event-driven programming)」をベースに作られているようです。GUI(Graphical User Interface)を利用するアプリなどではイベント駆動型プログラミングを使用することが多いみたいですが、イベントというのは2種類あるようで、

  • system events(受動的なイベント?)
  • user actions(ユーザによる能動的なイベント?アクション?)

です。

System eventsが今いち分かりませんが、例えば電話を着信したとか、メールを受け取ったとか、ユーザの行動とは無関係に「イベント」が起こったケースでしょうか?User actionsは自明で、画面をタップしたとか、マウスをクリックしたとか(スマホだとマウスは無いですが)。重要なのは、system eventsにしろuser actionにしろ、実際にアプリの操作をしているのは利用者(user)であって開発者(developer)ではないということで、開発者がやらなきゃいけないのは

ユーザがどんなアクションを実行できて、その結果何が起こるのか?

というのを決めることです。

アクションって何?

アクション(action or action method)は、アプリで起こったイベントに紐付けられているコードで、名前から推測出来るようにメソッドです。

ややこしいんですが、上で出て来たeventとしてのuser actionと、ここのaction methodは別物だと思います。そうでないと、event = actionとなって話がややこしくなります。

イベントが起こったことによって、コード(アクション)が実行される、という順序(因果関係)です。アクションを定義することで、データの操作をしたり、UIのアップデート(UIを動かしたり、切り替えたりすること?)をしたり、様々なことが出来るようです。

アクションとアウトレットの違いは?

アクションはアウトレットと同じように作ることが出来ます(control+ドラッグ)。アクションとアウトレットの違いは

アクションで紐付けるのはメソッド、アウトレットではプロパティ

という点です。View controllerファイルに作られたメソッドをストーリーボード上のオブジェクトと繋げると、ユーザがそのオブジェクト(UI)に対して操作を行った時にそのメソッドが呼び出される、ということになります。

では実際に簡単なアクションを作ってみます。今回実装するアクションは、

ユーザが「デフォルト料理名をセット」ボタンをタップすると、予め設定したデフォルト値をラベルにセットする

というものです。

ラベル用リセットアクションをViewController.swiftコードに作る

アクションを作る手順はアウトレットを作る場合とほぼ同じですが、最初なので少し丁寧に説明します。

アクションを追加する場所にコメントを書く

アウトレットの場合と同様に、これからアクションを追加する場所に特殊なコメントを書きます。追加する場所は、最後のカッコ}の手前と指定されていますので、そこに追加。

// MARK: Actions

違いは// MARK:の後が「Actions」になっていることです。これで、

このコメントより下にはアクション(メソッド)が列挙されてます

と言っています。

ボタンを選択して「control+ドラッグ」

「デフォルト料理名をセット」と書いてあるボタンをストーリーボード上で選択。次に、controlを押しながらドラッグしてViewController.swiftまで引っ張り、先程書いたコメント直下に落とします。

ダイアログボックスに入力

その後ダイアログボックスが出てくるので、下記の通り設定。

  • Connectionを「Action」にセット(デフォルトはOutlet)
  • Nameを「setDefaultLabelText」にセット
  • Typeを「UIButton」にセット(デフォルトはAnyObject
  • 残りの設定は触らずに「Connect」をクリック

Xcode: UIButtonにactionを設定
ダイアログボックスの「Connect」をクリックする直前が上の画像のような感じです。

You may have noticed that the value of the Type field defaults to AnyObject. In Swift, AnyObject is a type used to describe an object that can belong to any class. Specifying the type of this action method to be UIButton means that only button objects can connect to this action. Although this isn’t significant for the action you’re creating right now, it’s important to remember for later.

と書いてありますが、型をUIButton限定にすることで、このアクションと接続可能なオブジェクトを限定出来るようです。今回は余り関係ないみたいですが、後々のために覚えておくのが重要と書いてあります。何かしら用途を限定するようなアクションを実装するケースがあるのかもしれません。

「Connect」をクリックしてアクションを追加

ダイアログボックスで「Connect」をクリックして追加されたアクションが

    @IBAction func setDefaultLabelText(_ sender: UIButton) {
    }

です。

パラメータのsenderUIButton型で、これがストーリーボード上のオブジェクト(ここではボタン)を指しています。IBAction attribute(@IBAction)は、

このメソッドをInterface Builderでアクションとして使う

ということをXcodeに伝える識別子です。

追加されたアクションは空なので、ここにデフォルト料理名をセットするコードを書きます。

デフォルト料理名をセットするコードを実装

先程作ったメソッドの中に、デフォルト料理名をセットするコードを書くと、

@IBAction func setDefaultLabelText(sender: UIButton) {
    mealNameLabel.text = "デフォルトテキスト"
}

という感じになります。

変更したいのはラベルに表示されているテキストです。ストーリーボードのラベルはmealNameLabelと繋がってますので、これを使います。またテキストを文字列としてセットしたい場合は、UILabelが持っているプロパティtextを使います。

Xcodeには補完機能がついているので、

Xcode: UILabelのtextを補完して探す
という具合に、dot syntaxで「t」まで入力するとtから始まるプロパティやメソッドを補完してくれます。上の画像を見ると、textというプロパティはString?(optional String)なのが分かります。

Notice that you didn’t have to specify the type of Default Text, because Swift’s type inference can see that you’re assigning to something of type NSString and can infer the type correctly.

とあるように、型指定なしでもコンパイルエラーにならないのはSwiftの型推論のおかげです。Xcodeで補完される型を見てStringだと思っていましたが、NSStringという文字列型を使うと書いてあります。

シミュレータでテストしてみる

追加したアクションが機能するかシミュレータで確認します。シミュレータを起動して、「デフォルト料理名をセット」ボタンを押すと、

Xcode: ボタンにアクションを追加後のシミュレータテスト
おお!確かにラベルが「デフォルトテキスト」に変わりました。

ターゲット・アクションパターン

今実装した挙動はデザインパターンの1つで、target-actionと呼ばれている設計だそうです。これは、

ある特定のイベントが起こると、オブジェクトA(sender)がオブジェクトB(target)にメッセージを送る(というアクションが起こる)

というデザインパターンで、今回の例を具体的に当てはめると、

  • イベント = ユーザが「デフォルト料理名をセット」ボタンをタップすること
  • アクション = setDefaultLabelTextメソッド
  • target = ViewController(アクションが定義されている場所)
  • sender = 「デフォルト料理名をセット」ボタン

「メッセージを送る」の部分を今回の例で具体的に書くと、

「デフォルト料理名をセット」ボタンが、自身にひも付けされたアクションを実行するようtargetに伝える

でしょうか?Targetは実際にアクションを実装しているViewControllerです。

通常senderはイベントを起こすUI(ボタン、スライダー、スイッチなど)で、今回のケースだとボタンでした。イベントはユーザが起こした行動(タップ、ドラッグ、値を変更など)に反応して起こる、というのは直感的にも分かりやすいと思います。

このターゲット・アクションパターンというのはiOSアプリプログラミングでは非常に一般的で、この後も出てくるようです。

ユーザ入力を処理する | Process User Input

ここまでで、ボタンを押すと用意しておいたデフォルトテキストをラベルに表示する、というアクションを実装しました。次は、

テキストフィールドに入力した文字列を、ユーザがキーボードのReturnボタンをタップしたらラベルに表示する

というユーザ入力を処理するような機能を実装します。このような処理をするのに、delegation(委譲)というデザインパターンを使っています。

Delegation(委譲)デザインパターン

Delegation(委譲)というデザインパターンは、Wikipediaの記事を引用すると、

委譲を行うオブジェクトは委譲先オブジェクトへの参照を持ち、必要に応じてその参照を切り替える事で動作にバリエーションを持たせる事ができる。一種の実装遅延、プラグイン機構である。一例としては、オブジェクトの編集を行う時、編集の前処理、後処理を本処理と独立させ委譲先に任せる事で、オブジェクト本体の変更を最小限にとどめ局所性を向上させる、などがある。

ということのようです。Swiftの場合、delegationデザインパターンを実装するのにプロトコル(protocol)が良く用いられています。

今回の例ですと、

  • 委譲を行うオブジェクト(delegating object) = テキストフィールド(UITextField
  • 委譲先オブジェクト(delegate) = ViewController
  • 委譲用プロトコル = UITextFieldDelegate

となります。委譲用プロトコルにはメソッドの雛形を定義して、UIで何かしらの変更があった場合にアクションを起こすような状態にし、その具体的な中身を委譲先オブジェクトで実装する、というイメージです。

なぜdelegationなのか?というのは未だよく分かっていません。おそらくUIのイベントに対する処理を実装するのに、柔軟な実装が可能な手法の1つだから、と思っています。

UITextFieldのdelegateを定義したプロトコルはUITextFieldDelegate

テキストフィールドのdelegateを定義した委譲用プロトコルはUITextFieldDelegateです。そのままですね。テキストフィールドの処理を委譲したいクラスに、この委譲用プロトコルを採用(adopt)してみます。採用(adopt)と言っているのは、クラスの場合の「継承(inheritance)」のようなものです。

今回委譲先のdelegateとして使うのはViewControllerで、その理由はViewControllerがテキストフィールドの参照(nameTextField)を持っているからです。

UITextFieldDelegateプロトコルを採用する

ではUITextFieldDelegateプロトコルを実際に採用してみます。Assistant editorが開いている状態ならstandard editorを開いた状態に戻して、navigator areaとutility areaを開きます。ViewController.swiftが開いていない場合は、navigator areaのproject navigatorでViewController.swiftを開きます。

ViewController.swiftのclass宣言を

class ViewController: UIViewController {

から

class ViewController: UIViewController, UITextFieldDelegate {

に書き換えます。

Xcode: UITextFieldDelegateプロトコルを採用
UITextFieldDelegateプロトコルを採用した直後のViewControllerはこのようになってます。

委譲用プロトコル(ここではUITextFieldDelegate)をクラスに採用することで、ViewControllerをテキストフィールドのdelegateとして扱えて、テキストフィールドに入力された情報を処理する機能を実装することが出来ます。

ViewControllerをテキストフィールド(nameTextField)のdelegateとしてセットする

ViewControllerが持っているメソッドviewDidLoad()を探します。コードは短いのですぐ分かると思いますが、現在のviewDidLoad()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
}

このようになっているはずです。コメントは要らないので、コメントを消して次の記述をメソッドに追加します。

// Handle the text field’s user input through delegate callbacks.
nameTextField.delegate = self

selfは自分自身のことなので、ここでのselfViewControllerです。

nameTextFieldが持つdelegateというプロパティが何者なのか調べてみると、

weak var delegate: UITextFieldDelegate?

delegateプロパティはUITextFieldDelegateの弱参照(オプショナル型)であることが分かります。

調べる方法は色々ありますが、delegateを選択した状態でutility areaの「Quick Help Inspector」を開く、またはdelegateを選択した状態で右クリックして「Jump to Definition」に飛ぶ、等で見ることが出来ます。または、ブラウザでiOS developer libraryを開いてUITextFieldの定義ページに飛んでも良いかもしれません。

現在のviewDidLoad()

override func viewDidLoad() {
    super.viewDidLoad()

    // Handle the text field’s user input through delegate callbacks.
    nameTextField.delegate = self
}

のようになっているはずです。ViewControllernameTextFielddelegateになりました。

UITextFieldDelegateのメソッドを実装する前に

UITextFieldDelegateのメソッドは全てoptional

UITextFieldDelegateのメソッドは全てoptionalメソッドらしく、プロトコルを採用したクラスで実装しなくても良いようです。

まだ完全に理解してないですが、どうやらoptionalメソッドはObject-Cの名残のようで、これを使うには@objcという特別なattributeをクラスに付けないといけないみたいです。Object-Cに依存することになるので、Swift特有の構造体や列挙型では使えないようです。

ただし、今回作りたいアプリの挙動

テキストフィールドに入力した文字列を、ユーザがキーボードのReturnボタンをタップしたらラベルに表示する

を実現するためには、次の2つのメソッドを実装する必要があります。

func textFieldShouldReturn(_ textField: UITextField) -> Bool
func textFieldDidEndEditing(_ textField: UITextField)

UITextFieldDelegateのマニュアルからこれらのメソッドの説明を抜粋すると、

textFieldShouldReturn(_:)
The text field calls this method whenever the user taps the return button.

textFieldDidEditing(_:)
This method is called after the text field resigns its first responder status.

とあることから、これらのメソッドが呼び出されるタイミングは

  • textFieldShouldReturn(_:)はReturnキーがタップされた時
  • textFieldDidEditing(_:)はテキストフィールドがfirst responderじゃなくなった時

ということになります。

First responder

First responderというのはイベントを一番最初に受け取るオブジェクトのことのようで、アプリ上でアクティブになっているUI(オブジェクト)だと思えば良いでしょうか?どのオブジェクトでもそうですが、適切なタイミングでfirst responderの状態(アクティブな状態)を解除してやらないとユーザが困るかもしれません。例えば、テキストフィールドの編集が終わったのに、キーボードが出たままの状態になってるとか。

この制御をするのがUITextFieldDelegateの役割の1つで、first responder解除はresignFirstRespodner()というメソッドで行うようです。First responder解除のタイミングは色々あるかと思いますが、今回のケースでは

ユーザがキーボード上でReturn(Done)ボタンをタップした時、first responder状態を解除する

とするようです。

UITextFieldDelegateプロトコルのメソッドtextFieldShouldReturnを実装する

このメソッドにはfirst responder状態を解除するテキストフィールドのメソッドresignFirstResponder()を実装します。1つずつ順番に追加していきます。

Delegate用コメントを追加する

アウトレットやアクションの場合と同様に、

// MARK: UITextFieldDelegate

というコメントを追加します。

Xcodeには「function menu」という機能があって、これまで追加したプロパティやアクションのリストを見ることが出来ます。Function menuを開くには、editor area上部にあるファイル名が表示されている部分(ここではViewController)をクリックします。

Xcode: Function menuを表示
クリックするとプロパティ等がリスト表示される小窓が出て来ます(上の画像参照)。これを見ると、「Properties」や「Actions」という項目が表示されていますが、これが// MARKから始まるコメントで追加した部分に対応しています。ここから例えば「Properties」を選択すると、その場所まで飛ぶことが出来るので便利です。

メソッドtextFieldShouldReturnを追加する

メソッドtextFieldShouldReturn(_:)を追加します。

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
}

中身は次で追加します。このメソッドは名前が長いですが、Xcodeの補完機能を使うと簡単に挿入出来るので便利です。

Xcode: textFieldメソッドの補完
上の画像は「func textField」までタイプした状態で、Xcodeがメソッドを補完してくれているのが分かります。候補が複数出て来た場合は、キーボードの上下でメソッドを選択してTabを押すと補完してくれます。それでも候補が絞れない場合は、さらに選択してTabの繰り返しです。

補完が出来た場合は、上記のようなコードが一気に挿入されます。

textFieldのresignFirstResponderメソッドを呼び出す

先程外枠だけ作ったメソッドの中に、

// Hide the keyboard.
textField.resignFirstResponder()

を追加します。resignFirstResponder()の部分もXcodeが補完してくれます。

戻り値をセットする

最後に戻り値をセットします。メソッドの宣言を見直すと、

func textFieldShouldReturn(_ textField: UITextField) -> Bool {

ですから、戻り値はBoolです。したがって、

return true

と書きます。

最終的にtextFieldShouldReturn(_:)メソッドは、

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    // Hide the keyboard
    textField.resignFirstResponder()

    return true
}

のようになるはずです。これで、ユーザがReturnキーをタップするとテキストフィールドのfirst responder状態が解除されてキーボードが隠れる、という挙動を実装出来たはずです。

UITextFieldDelegateプロトコルのメソッドtextFieldDidEndEditingを実装する

次に追加するメソッドの中身で、テキストフィールドに入力されたテキストをラベルのテキストに渡します。

メソッドtextFieldDidEndEditingを追加する

先程と同様に、まずメソッドの外枠をViewControllerに追加します。

func textFieldDidEndEditing(_ textField: UITextField) {
}

Xcodeの補完機能で簡単に挿入できるはずです。

ラベルのテキストに、テキストフィールドから入力されたテキストを渡す

タイトルのままですが、ラベルのテキストプロパティにテキストフィールドのテキストプロパティを代入します。

mealNameLabel.text = textField.text

最終的にメソッドtextFieldDidEndEditing(_:)

func textFieldDidEndEditing(_ textField: UITextField) {
    mealNameLabel.text = textField.text
}

のようになっているはずです。

シミュレータでテキストフィールドのテスト

ではシミュレータを起動して、実装した機能がちゃんと動くかどうか確認してみます。

Xcode: シミュレータでテキストフィールドに設定したアクションのテスト
テキストフィールドに文字列を打ち込んで(ここでは「ごはん」)Returnキーを押すと、確かにラベルが変化し、さらにキーボードが隠れることが分かります。上の画像はReturnキーを押した後の画面で、確かにキーボードが表示されていないのが分かります。もしシミュレータで、テキストフィールドをタップした際にキーボードが出てこない場合は、⌘+Kを押すと出て来ます。

まとめ

今回は、これまで作ってきた基本的なUIに対して、ソースコードを繋いでユーザ入力を処理する機能を実装しました。大分アプリっぽくなってきた感じがします。

Part4では、ViewControllerをさらに加工して、UIに写真を表示させます。

「Start Developing iOS Apps (Swift)」でFoodTracker Appを実際に作ってみる その4[View Controllersを加工する]
公式にある「Start Developing iOS Apps (Swift)」を使って実際にFoodTracker Appを作ってみる、と...
スポンサーリンク
広告1
広告1

シェアする

フォローする