「Start Developing iOS Apps (Swift)」でFoodTracker Appを実際に作ってみる その2 [基本的なUIを作る]

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

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

「Start Developing iOS Apps (Swift)」でFoodTracker Appを実際に作ってみる その1
Swiftのプログラミング言語としての構造や書き方は学んでいますが、実際にアプリを組む時に必要になるUI周りの学習も必要です。ここでは、公式...

前回では一通りSwiftの基本を学びました(復習も含む)。今回から、いよいよXcodeを使ってUIの基本を学んでいきます。ちなみにUIはUser Interface(ユーザインタフェース)の略です。ここでの目標はとにかくXcodeに慣れることです。

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

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

目次

基本的なユーザインタフェース(UI)を作る | Build a Basic UI

再び「Learning Objectives(学習目的)」が列挙されていますので、書き出してみます。

  • Xcodeでプロジェクトを作成する
  • Xcodeプロジェクトテンプレートで作られる重要なファイルの機能を知る
  • プロジェクト内にあるファイルを開いたり、ファイル間を移動する
  • シミュレータでアプリを走らせる
  • ストーリーボードでUIエレメントを追加、移動、リサイズする
  • Attributes inspectorを使ってストーリーボード内のUIエレメントの設定を編集する
  • Outline viewを使ってUIエレメントの階層構造を見たり、並べ替えたりする
  • Preview assistant editorを使ってストーリーボードの実機表示をプレビューする
  • Auto Layoutを使ったUIのレイアウト

後半は英語が多く分かりにくいですが、Xcodeを使っていくと理解出来るはず、だと思います。Part2が終わった時点で、アプリの見た目は

Part2が終わった後のFoodTrackerアプリの見た目

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

新しいプロジェクトを作成 | Create a New Project

アプリケーションフォルダからXcodeを開く

新しいプロジェクトを作るためにXcodeを開きます。アプリケーションフォルダから選択しても良いですし、spotlight(⌘+space)から「Xcode」と打ち込んでも開くことが出来ます。

Xcodeを開いた直後

始めてXcodeを開いた場合はこのウィンドウが出てくると思います。出てこない場合の対処方法は次のステップで。

「新しいプロジェクトを作る」を選択

新しいプロジェクト(project)を作ります。

上記画像のwelcomeウィンドウが出ている場合は、左下にある3つの項目の真ん中

Create a new Xcode project

をクリックします。

もし以前にプロジェクトを作るなり、playgroundで遊ぶなりしてwelcomeウィンドウが出てこないような設定にしていた場合は、メニューから

File > New > Project…

を選択します(またはキーボードショートカットshift+⌘+N)。下の画像も参照。

Xcode: メニューから新しいプロジェクトを作る

「iOS」のApplication、「Single View Application」を選択

新しいプロジェクトを作るためのテンプレートを選びます。ここで作るFoodTrackerアプリはiOSアプリなので、最上部のメニューから

iOS

を選択します。

Xcode: 新プロジェクトのテンプレートを選択
上の画像のように「Application」と「Framework & Library」という項目がありますが、今作りたいのは「Application」ですから、その項目の左上

Single View Application

を選択。選択したら、右下の「Next」をクリック。

新しいプロジェクトの名前やオプションを選択

次は、新しいプロジェクトの名前などを決めます。

Xcode: 新しいプロジェクトの名前やオプションを選択

  • Product Name: プロジェクトとApp自体の名前になります。ここでは「FoodTracker」
  • Organization Name: 組織や個人の名前。空欄でも良いようですが、今回はサイト名から「Swiftrithm」を入れました
  • Organization Identifier: サイト持ちの場合、ドメインを逆にする(ここでは「com.swiftrithm」)のが通例のようです。なければcom.example
  • Bundle Identifier: product nameとorganization identifierから勝手に決まります
  • Language: 「Swift」or「Object-C」を選択。当然「Swift」です
  • Devices: 「Universal」(iPhoneとiPadの両方で使えるApp)を選択。iPhoneやiPadに限定することも可能
  • Use Core Data: 選択しない
  • Include Unit Tests: 選択する
  • Include UI Tests: 選択しない

選択し終わったら右下の「Next」をクリック

保存先を決めて「Create」をクリック

ここまで進めると、下の画像のようなワークスペースウィンドウ(workspace window)が出てくるはずです。

Xcode: プロジェクトの保存先を決めた直後のワークスペース

In the workspace window, you may or may not see a warning triangle with a message that says “No code signing identities found.” This warning means you haven’t set up Xcode for iOS development yet, but don’t worry, you can complete these lessons without doing that.

に書いてあるwarning(警告)「No code signing identities found」が出てますが、ここでは関係ないようなので無視して先に進みましょう。

Xcodeに慣れる | Get Familiar with Xcode

Familiar withは「精通する」とか「熟知する」という意味ですが、いきなりは厳しいので「慣れる」としました。使っていけばそのうち精通すると思うので、まずは慣れることです。

Xcode: ワークスペースウィンドウ概観

どうせ後から何回も使うことになるので、各セクション毎の名前を覚えておく位で次に行きましょう。以下の説明は現在持っている知識や見た目で書いているので間違っているかもしれません。

  • Toolbar: 一番上にあるバー。シミュレータ回りのボタン(左)、真ん中には名前や日付の情報、右にはワークスペース自体の表示コントロールボダン
  • Navigator area: プロジェクトを作るファイルやフォルダをツリー表示
  • Editor area: ファイルの編集等
  • Utility area: 細かいオプションの選択等々

「Toolbar」などの名称はそのまま英語で使うことにします。

シミュレータを起動してみる | Run Simulator

まだ何も触ってないですが、実はこの時点でアプリケーションをビルドし、シミュレータ(Simulator)を起動してアプリの画面を見ることが出来ます。Xcodeが基本的なアプリの環境等を自動でセットアップしてくれるからだそうです(万歳)。

シミュレータはiOSの色んなデバイス(iPadやiPhoneの機種毎)をモデルすることが可能ですから、アプリを開発したい特定のデバイス上でのテストも簡単に出来ます。公式サイト上ではiPhone6をテストしていますが、ここではiPhone 6s Plusでシミュレーションしてみましょう。

ToolbarでiPhone6s Plusシミュレータを選択

Toolbarの左側に「FoodTracker > iPhone 6s Plus」とか表示されている枠があると思います。

Xcode: ToolbarのScheme pop-up menuでデバイスを選択

この枠は「Scheme pop-up menu」と呼ばれています。ここをクリックすると、

Xcode: Scheme pop-up menuでデバイスを選択

このようにシミュレータでテスト可能なデバイスが表示されますので、この中から好きなデバイスを選択することが可能です。

プロジェクトをビルド&シミュレータを起動

では早速シミュレータを起動してみます。

先程の「Scheme pop-up menu」ボタンの左側に2つボタンがあります。一番左の再生ボタンみたいな形をしたボタンが「Run button」です。

Xcode: Run buttonとactivity viewer

この「Run button」を押すとプロジェクトをビルドして、ビルドが成功するとシミュレータを起動します。

メニューからビルドを開始するには、

Product > Run

を選択。キーボードショートカットなら⌘+Rです。

Xcode: メニューからシミュレータを起動

Toolbarのactivity viewerでビルドの経過を見る

Run buttonを押すとToolbarの真ん中にある「activity viewer」にビルドの経過が表示されます。うまく行くと、activity viewerの最終的な表示は

Running FoodTracker on iPhone 6s Plus

になるはずです。

ビルドが完了するとシミュレータが自動的に立ち上がるはずです。シミュレータの起動時は、実際にiOSデバイスが起動しているような画面になります(起動にはしばらく時間が掛かります)。最終的には

Xcode: Simulatorを起動

のような真っ白な画面になるはずです。今はiPhone(iPhone 6s Plus)でシミュレーションしているので、iPhoneで見るような縦長の画面が出て来ます。始めてシミュレータが起動した時は感動しますね。

シミュレータを終了する時は、メニューから

Simulator > Quit Simulator

を選択、または(シミュレータを選択してから)キーボードショートカット⌘+Q

ソースコードを見てみる | Review the Source Code

ここから少し中身に入っていきます。Xcodeのプロジェクトを作る時に「Single View Application」テンプレートを選択しましたが、navigator areaを見るとデフォルトで用意されているファイルが幾つかあります。まずは「.swift」の拡張子がついたファイル

  • AppDelegate.swift
  • ViewController.swift

を順番に見ていきます。

ソースファイルの中身を見る方法

Navigator area(左側にあるメニュー)はデフォルトで開いている状態ですが、もしnavigator areaが隠れている場合はToolbarの一番右側にあるボタンを確認。

Xcode: Navigator areaの表示・非表示切り替え

3つあるうちの左側のボタンが青色になっていればnavigator areaが表示されているはずです(青じゃなくて灰色ならクリックしてアクティブにする)。

Project navigatorを選択してファイル階層を表示

Xcode: Project navigatorでプロジェクトのソースファイルを表示

Navigator areaでのデフォルト表示はproject navigatorと呼ばれる部分が選択されていて、プロジェクトのソースファイル階層構造が表示されているはずです。もしそうなっていない場合は、project navigatorの部分をクリック。

メニューから選択したい人は

View > Navigators > Show Project Navigator (またはキーボードショートカット⌘1)

必要ならフォルダの中身を展開

Finderと同じような見た目なので使い方はなんとなく分かると思います。もし「FoodTracker」の中身が表示されていない場合は、FoodTracker横の三角マークをクリックして展開。

AppDelegate.swiftを選択

見たいファイルを選択します。ここではAppDelegate.swiftを選択。そうすると、真ん中のeditor areaにAppDelegate.swiftの中身が表示されます。別窓で開きたい場合はダブルクリック。

AppDelegate.swiftファイル

Xcodeプロジェクトでソースコードを見る
上の画像はソースコードを開いた時のスクリーンショットです。Editor areaで見ることも出来ますし、上述のようにnavigator areaに表示されているファイル名(AppDelegate.swift)をダブルクリックして別窓にソースコードを開くことも可能です。

「AppDelegate.swiftが持っている重要な役割が2つある」と書いてありますが、要約すると

  • アプリの開始点作成。デバイスからのイベント(タップなど)を渡す仕組み(run loop)を作る(UIApplicationMain)
  • 内容表示窓作成&状態遷移の管理(AppDelegate)

です。図でまとめると、

AppDelegate.swiftの概念図

こんな感じ。説明が足りない部分があるので以下で補足します。

UIApplicationMain attributeについて

@UIApplicationMainという見慣れない表記がありますが、これは

  • UIApplicationMain()という関数を呼び出す
  • UIApplicationMain()関数内部のパラメータの1つであるdelegate class名にAppDelegateを使う

という処理をしているようです。今の所C言語のmain関数的な役割をしているものと理解しています。一応公式マニュアルの@UIApplicationMainの記述を抜粋

“UIApplicationMain
Apply this attribute to a class to indicate that it is the application delegate. Using this attribute is equivalent to calling the UIApplicationMain function and passing this class’s name as the name of the delegate class.

If you do not use this attribute, supply a main.swift file with code at the top level that calls the UIApplicationMain(_:_:_:) function. For example, if your app uses a custom subclass of UIApplication as its principal class, call the UIApplicationMain(_:_:_:) function instead of using this attribute.”

抜粋:: Apple Inc. “The Swift Programming Language (Swift 3)”。 iBooks https://itun.es/jp/jEUH0.l

UIApplicationMainの役割

UIApplicationMain関数はC言語で言う所のmain関数

と書きましたが、つまりプログラムが開始した時に最初に実行される場所(エントリーポイント、entry point)になります。またiOSデバイスから受け取った「イベント(event)」を仕訳するrun loopと呼ばれる仕組みを作ることも、UIApplicationMainの仕事の1つのようです。

イベント(event)は今いちよく分かってません。例えば「iOSのデバイス画面をタッチすること」はイベントの1つのようですが、当然それだけじゃないみたい。また、run loopというのもよく分かりませんが、

An event processing loop that you use to schedule work and coordinate the receipt of incoming events in your app.

という説明しかないので、ここでは掘り下げません。

さらに、UIApplicationMainはオブジェクトを2つ

application object (UIApplication)とapp delegate (AppDelegate)

作っています。

UIApplicationの役割

UIApplicationの役割は

UIApplicationMain creates an application object that’s responsible for managing the life cycle of the app

と記述されているように、アプリのライフサイクルを管理すること、だそうです。しかし、ここではこれ以上具体的な説明がないので、正直何をしているのか良く分かりません。

UIApplicationの説明をiOS Documentationで読むことが出来ます。AppDelegate.swiftにあるUIApplicationDelegateをクリックすると右側のUtility areaに説明が出て、その中にUIApplicationへのリンクが含まれているのでクリック。英語です。理解できたら、ここに追記するか、別記事で掘り下げようと思います。

AppDelegateの役割

AppDelegateに関しては少し詳しく記述されていて、その役割は

  • アプリの内容を描画するための窓を作る
  • アプリ内部の状態遷移に応答する

ことのようです。

1つ目はAppDelegateが持っているプロパティ

var window: UIWindow?

のことです。

2つ目の状態遷移というのは、アプリが起動したり、バックグラウンドに移動したり、またはアプリが終了したりする場合など、アプリの状態が変化することです。ソースコードのメソッドの名前を見ると確かに、

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
func applicationWillResignActive(_ application: UIApplication)
func applicationDidEnterBackground(_ application: UIApplication)
func applicationWillEnterForeground(_ application: UIApplication)
func applicationDidBecomeActive(_ application: UIApplication)
func applicationWillTerminate(_ application: UIApplication)

「DidEnterBackground」(バックグラウンドに移った)とか「WillTerminate」(これから終了する)とか、状態遷移に関する名前になっています。基本的にはapplication objectがちゃんとこれらのメソッドを呼び出してくれるようなので、ユーザは特に何もしなくても良いようです。

Use these method templates to add custom code that will execute when the methods are called. In this lesson, you won’t be using any custom app delegate code, so you don’t have to make any changes to the AppDelegate.swift file.

とありますが、何かしら状態遷移をカスタマイズしたい場合は、これらのメソッドを使うことになります。FoodTrackerアプリではココの部分は触らないようですので、デフォルトのAppDelegate.swiftを使います。

ViewController.swiftファイル

XcodeでViewControllerのソースコードを見る
Single View Applicationに含まれるもう1つのファイルがViewController.swiftです。上の画像はViewController.swiftファイルをクリックして、中身をeditor areaで開いた状態です。

ソースコードを見ると分かりますが、ViewControllerクラスはUIViewControllerを継承して、メソッドをオーバーライドしているだけで全くカスタマイズされていません。

不要なメソッドの削除

ここでは2番目のメソッドdidReceiveMemoryWarning()が必要ないようなので消します。したがって、現時点でのViewController.swiftの中身は、

import UIKit

class ViewController: UIViewController {

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

}

のようになっているはずです。ここではViewController自体には全く触れていませんが、後で編集する時に出てくると思います。

ストーリーボードを開く | Open Your Storyboard

Xcodeでストーリーボードを開く

Navigator areaで3番目に表示されている「Main.storyboard」を選択すると、上記画像のような画面になるはずです。これをストーリーボードと呼びます。

ストーリーボード | Storyboard in Xcode

ストーリーボード(storyboard)はアプリのUIを視覚的に表現したツールで、アプリのコンテンツそのものや、画面がどう遷移するかが一目で分かります。ストーリーボードの利点は、配置などを目で見ながら直感的に決められることです。

ストーリーボードを編集するツールがInterface Builderと呼ばれていて、GUIを使ってテキストやボタンを配置することが出来ます。ストーリーボードの背景(白地の部分全体)をcanvas(キャンバス)と呼びます。

ストーリーボードのScene

ストーリーボード上で見えている画面をscene(シーン)と呼びます(上記画像ではViewControllerの部分全体)。Sceneの左側に右向きの矢印が付いていますが、この矢印の左側には何もありません。つまり、今あるsceneはアプリが起動した際に一番最初に表示される画面ということになります。これをstoryboard entry pointと呼びます。

ストーリーボード上のcanvasで見えているsceneは必ずしも指定したデバイスの大きさ(ここではiPhone 6s Plus)と一致している訳ではありません。

This is because the scene on the canvas is a generalized representation of your interface that can apply to any device in any orientation.

と書いてあるように、あらゆるデバイスのあらゆる向きに対応出来るように、canvas上のsceneは一般化された表現になっているようです。

Xcode 8になってから、ストーリーボード上の表示に対して特定のデバイスを指定することが可能になりました(例えばiPhone 6sとして表示)。ですから、この後出てきますが、(以前のXcodeで発生していたような)UIを置いた時のストーリーボード上で見える表示と実機表示のミスマッチが起こりにくくなっています(起こらないかも)。

基本的なUIを作ってみる | Build the Basic UI

では、いよいよ基本的なUIを実際に作ってみます。まずやることは、

ユーザが(新しい料理の)写真をFoodTrackerに加えることが出来るようなUIを作ること

です。

Views: 実際に表示されるUIの要素

ストーリーボード上で作ることの出来るUIは、Xcodeが予め用意してくれてるライブラリから持ってきます。ボタンやテキスト領域なんかは実際にUIとして表示されるものですが、例えばview controllersなどはアプリの挙動を決めるためのモノでスクリーンには表示されません。

ボタンやテキストなどのような画面に表示されるようなUIの要素をViewsと呼びます。アプリのUIを形作っているのがviewsだと言えます。iOSにある全てのview objectsはUIView型、もしくはそのサブクラスになっています。例えばUITextFieldは、UIViewのサブクラスで、テキスト領域をUIに表示してユーザが何かしらのテキストを入力できる要素になります。

では実際にUITextFieldを使って、料理の名前を付けられるUIを作ってみます。

UITextFieldでテキスト入力領域を作ってみる

Object Libraryを開く

Xcodeが用意しているライブラリがObject Libraryで、ユーティリティエリア(utility area)の一番下に表示されています。

XcodeでObject Libraryを開く

Object libraryが開いていると、上記画像のような表示になっているはずです。もしこの表示になっていない場合は、上にある4つのボタンの左から3つ目(丸の中に四角が入っているボタン)を押して下さい。メニューから開きたい場合は、

Views > Utilities > Show Object Library

またキーボードショートカットはcontrol+option+⌘3です。

Object libraryを見ると、viewsがリスト表示になっていて、それぞれの名前や説明、見た目が表示されています。

テキストフィールド(text field)を探す

テキストフィールド(text field、UITextField)を探す場合、object library上にポインタを載せて上下に動かせばライブラリ内をスクロール出来るので、text fieldと表示されているモノを探しましょう。

または「Filter」と表示されているobject library内部の検索機能を使うと早く探せます(上記画像の一番下に表示されている検索窓)。

Xcodeのobject library内で検索

これはfilterで「text」を検索した結果ですが、text fieldとtext viewが表示されているのが分かります。

テキストフィールドをobject libraryからsceneへドラッグする

テキストフィールドが無事見つかったので、これをsceneへドラッグします。

Xcodeでtext fieldをobject libraryからsceneへドラッグ

上の画像はテキストフィールドをobject libraryからドラッグしてsceneの上まで持ってきた状態です(まだ落としていない)。テキストフィールドをドロップする(マウスorトラックパッドから手を離す)と

Xcodeでtext fieldをsceneに設置

このようにテキストフィールドが実際に設置されます。

テキストフィールドを左マージンの位置まで移動する

公式のレッスンではドラッグしつつ左マージンの位置まで移動してから設置、という指示でした。私は適当にテキストフィールドを設置してしまったので、すでに設置したテキストフィールドを左マージンの位置まで移動します。

margin(マージン)は「余白」「欄外」という意味。余白より外側は実機では表示されないのか?

Xcodeでtext fieldをscene内で移動

テキストフィールドをドラッグして移動すると分かりますが、ある程度左に寄って行くと左マージンの位置でscene上に縦の青い点線が表示されます(上の画像参照)。これが左マージンの位置だと思います。この青い線が表示された位置にテキストフィールドを設置します。上下の位置は「top half of the scene」との指示なので、やや上寄りに置きます。

また、この青い点戦はレイアウトのための表示で、オブジェクト(テキストフィールドなど)をドラッグしている間だけ表示されます。実際にテキストフィールドを設置すると分かりますが、ドラッグをやめると青い線は非表示になります。

テキストフィールドをクリックしてresize handlesを表示(もし表示されていない場合)

タイトルが英語で難しそうですが、単にテキストフィールドをクリックするだけです。

Xcode: text fieldをクリックしてresize handlesを表示

Resize handlesというのは、私も初めて名前を知ったのですが、テキストフィールドの左右に表示されている白抜きの小さい四角のことです(上の画像参照)。これはオブジェクトのサイズを調整する時に出てくる表示ですよね。

今回はテキストフィールドをドラッグして置いた段階でresize handlesが表示されているはずなので、改めてテキストフィールドをクリックする必要はないかもしれません。

テキストフィールドの左右の長さを変更する

ではresize handlesを使ってテキストフィールドの左右の長さを変更します。今左端は左マージンの位置に合わせてあるはずなので、右のresize handlesを掴んで右マージンの位置まで引っ張ります。

Xcode: Resize handlesを使ってtext fieldの長さを変更
左右の位置がマージンの位置にピッタリ合っていれば、上の画像のように左右マージンの位置と真ん中に青い点線が表示されるはずです(ドラッグしている間だけ)。これでテキストフィールドの設置は完了です。

設置は完了しましたが、今の状態だとテキストフィールドは真っ白でユーザは何を入力して良いのか分かりません。次は、

ユーザにはテキストフィールドに何を入力して欲しいのか?

という情報を知らせるための表示(text field’s placeholder text)を作ります。今回はFoodTrackerアプリですので、ここで入力して欲しい情報は「料理の名前」になります。

Text field’s placeholder textの設定方法

テキストフィールドを選択した状態で、ユーティリティエリア(utility area)のattributes inspectorを開きます。Object libraryは下側にありましたが、attributes inspectorはその上の縦長領域に出て来ます。

Xcode: Utility areaでattribues inspectorを表示
Attributes inspectorが表示されていると、上の画像のような表示になっているはずです。もし表示されていない場合は、上に並んでいる6つのボタンの左から4番目を押して下さい。

Attributes inspector内で「Placeholder」と表示されている場所を探して、そこに

Enter meal name

と入れます。日本語にするなら「料理名を入力」でしょうか?Placeholderにテキストを入力してリターンキー(enter)を押すと、

Xcode: text fieldにplaceholderを入力した状態

テキストフィールド内部に薄い灰色で「料理名を入力」と表示されているのが分かります。

テキストフィールドキーボードの設定 | Configure text field’s keyboard

ユーザがアプリ内でテキストフィールドを選択するとキーボードが表示されますが、キーボード周りの設定もattributes inspectorで設定可能です。テキストフィールドが選択されていることを確認して、attributes inspectorを開き、次の設定をします。

  • Return keyを「Default」から「Done」に変更
  • Auto-enable Return keyのチェックボックスを選択(ONにする)

Xcode: text fieldのkeyboard configurationをattributes inspectorで行う

変更後の状態が上の画像。Return keyを「Done」にしておくと、「入力が終わったら押せば良いんだな」という印象をユーザに与えることが出来ます。さらに、Auto-enable Return keyはテキストフィールドに何かしら文字が入力されないと、リターンキーが押せなくなるオプションです。したがって、これをONにしておくことで、空白文字(テキストフィールドに何も入力しない状態)で完了することが出来なくなります。

ラベルの設置

さらにユーザフレンドリーにするために、テキストフィールドの上にラベル(UILabel)を設置して、そこに「料理名」と書くことにします。テキストフィールドを置いた時と同じような手順でラベルを置きます。Object library上でのラベルは

Xcode: Object library内のlabelを検索

このような表示です。「Label」と書いてあるのですぐ分かると思います。ラベルをsceneに設置するまでの手順は以下の通り。

  • Object libraryのfilterで「label」とタイプ。Labelオブジェクトを探します
  • ラベルを見つけたらsceneにドラッグ
  • ラベルの位置はテキストフィールドの上。ラベルの左側をテキストフィールドの位置と揃えます(左マージンの位置に設置)
  • ラベルの上下の位置は、ドラッグした時ラベルの下側に青い点線が表示される位置

この時点でscene上での表示は

Xcode: Textfieldの上にlabelを追加した状態

のようになっているはずです。ラベルのデフォルト表示テキストは「Label」なので、これを「料理名」変更します。

  • 「Label」をダブルクリック、「料理名」にテキストを変更
  • リターンキーを押して完了
Attributes inspectorでAlignmentを左寄せにしておかないと、ラベルにテキストを入力した時に、ラベルの左右の位置がズレるかもしれません

ラベルとテキストフィールドを追加した後の最終的な表示は

Xcode: Labelとtext fieldを設置した後

のようになります。UIが上に寄りすぎていたので、少し下げました。また、もしラベルに文字を入力した際に左右の位置がずれた場合は、改めて左マージンの位置にラベルの左端が来るように位置を調整します。

ボタンの設置

次はボタン(UIButton)の設置です。ボタンはその名前の通り、ユーザがタップすることで何かしらのアクションを起こせるオブジェクトです。この「何かしらのアクション」を我々が定義することになります。

ここではボタンをただ置くだけで、アクションを設定しませんが、後で

ボタンをタップするとテキストフィールドに入力した内容をデフォルトに戻す

という仕様を実装します。

ボタンもテキストフィールドやラベルと同様で、object libraryから引っ張ってきます。

  • Object libraryでbuttonとタイプして探す
  • ボタンをsceneにドラッグ
  • テキストフィールドの下に設置。左寄せ(左マージン位置に合わせる)、ボタンの上に青い点線が出る位置に上下を合わせる
  • ボタンをダブルクリックして「デフォルト料理名をセット」とタイプ
  • リターンキーを押して完了

ボタン設置後の状態が

Xcode: Buttonオブジェクトをsceneに追加

このようになります。

アウトラインビュー | Outline view

Scene上で追加したUI elementsがどのように整理されているかを確認出来るツールがoutline viewです。

Xcode: Outline view

Editor areaの左下にある小さいボタンでoutline viewのON/OFF切り替えが可能です。デフォルトでは表示されているはずですが、outline viewが見当たらない場合はこのボタンをクリックすると表示出来るはずです。

Outline viewを見るとストーリーボード上のオブジェクトの階層構造が見て取れます。今自分で追加したオブジェクトは、テキストフィールド、ラベル、ボタンの3つですが、outline viewを見るとちゃんと追加されているのが分かります。

Xcode: Outline view (zoom)

私達が追加した3つのオブジェクトは「View」というviewsの下に配置されています。このようにviewsはUIとしてだけじゃなくて、他のviewsの入れ物としての機能も持っています(フォルダやディレクトリを想像すると分かりやすいかもしれません)。Viewsの階層構造で、親のviewをsuperview、親の下にあるviewsをsubviewと呼びます。今回のケースですと、

Viewがsuperview、その下にあるテキストフィールド、ラベル、ボタンがsubviews

になります。この例のように、1つのsuperviewに対して複数のsubviewsを持つことが可能です。

また、各sceneの最上位にあるviewのことをcontent viewと呼びます。今回の例だと、Viewがcontent viewということになります。

インターフェースのプレビュー | Preview Your Interface

Assistant editorという機能を使うと、アプリインターフェースが実機でどのように見えるかを確認することが出来ます。では実際にやってみましょう。

Assistant editorボタンをクリック

Xcode: Assistant editorのボタン

Assistant editorのボタンはtoolbarの右側、丸が2つ重なったようなボタンになります。Editor areaが分割された右側にassistant editorが表示されます。通常このままだと画面が狭すぎるので、navigator areaやoutline view、またはutility areaを隠すことで画面を広く使えます。各エリアの表示・非表示はtoolbarの一番右にあるボタンで行います。

Xcode: Editor areaとassistant editorのみ
Editor areaとassistant editorだけの表示にした画面が上の画像のようになります。Toolbar右側のボタンが全部灰色になっているのが分かります(navigator areaとutility areaを隠した状態)。

Assistant editorにストーリーボードのプレビューを表示

今assistant editor領域にはViewControllerのソースファイルが表示されているので、これをストーリーボードのプレビュー画面に切り替えます。切り替えはassistant editor画面上部にある

Automatic

と表示されている部分をクリック。そうすると、上記画像のようなメニューが出てくるので、

Preview > Main.storyboard (Preview)

を選択します。

そうすると、下の画像のような表示になるはずです。

Xcode: Assistant editorでinterfaceを確認

補足: Xcode 7.3(またはそれ以前)の場合

Xcode 7.3(またはそれ以前のバージョン)の場合は、assistant editorで実機のプレビューを見ると、

Xcode: Assistant editorでinterfaceを確認
一瞬良さ気ですが、テキストフィールドの右側が思いっきり画面からはみ出しています。ストーリーボード上では良さそうでしたが、実機での表示は問題がありそうです。

実機でのレイアウトには調整が必要 → Auto Layout

Xcode 7.3の場合の記述です。Xcode 8の場合表示はずれていませんが、レイアウトの調整は行います。

As you learned earlier, you’re actually building an adaptive interface that scales for different sizes of iPhone and iPad. The scene you see by default in your storyboard shows a generalized version of your interface.

とあるように、ストーリーボード上での表示はあくまでもインターフェースの一般的な表示であって、実際に実機で見える表示ではありません。

上の画像だと「iPhone 4-inch」の画面上でどう見えるかのテストですから、iPhoneの場合にはテキストフィールドの幅を狭くする、という設定が必要かもしれません。逆にサイズのデカイiPadだと、もしかしたらテキストフィールドを伸ばさないといけないかもしれません。

このような設定を実装出来るのがAuto Layoutだそうなので見ていきましょう。

Auto Layoutの適用 | Adopt Auto Layout

Auto Layoutとstack view

Auto Layoutというのは、「このscene上で、このUI elementの配置をこうしたい!」というユーザの意図を簡単に実装してくれる機構、だそうです。実際にユーザが記述するのはconstraintsというパラメータで、具体的には

  • このelementは別のelementに対してどう配置するべきか
  • このelementのサイズはどれくらいか
  • 全体のスペースが縮んだ時、2つのelementの内どちらが先に縮むか

等々、UI elementの大きさ・配置に関する制限(constraints)を指定することが出来ます。

また、「複数のUI elementに対する配置を同時に指定したい!」というケースが必ずあります。今回もまさにそうで、テキストフィールドとラベルとボタンを一括りにして、全体のスペースに対する配置を指定する必要があります。これを可能にするのがstack view (UIStackView)と呼ばれる機能です。これは使っていくと分かると思うので、早速やってみましょう。

「Stack」は「(物の)積み重ね、堆積、山」という意味なので、直訳すると「オブジェクトを積み重ねたもの」または「オブジェクトをグループ化したもの」という意味合いでしょうか

Auto Layout constraintsをsceneに追加する方法

Standard editor表示

「Standard editor」を押してassistant editorを非表示にして、navigator areaとutility areaを再度表示します。

Xcode: Standard editorを選択

Standard editorのボタンは、toolbar上でassistant editorボタンの左側にあります。

Xcode: Auto layoutを設定するため、standard editor表示
上の画像が元に戻したXcodeの状態です。

Stackしたいオブジェクトを選択する

次に、stack viewを使ってテキストフィールド、ラベル、ボタンを1つにまとめます。方法は2つあって、

  • Shiftを押しながらそれぞれのオブジェクトをクリック、
  • またはポインタをドラッグしてstackしたいオブジェクトを選択

です。なぜか前者しか紹介されていませんが、ポインタをドラッグして選択する方が簡単です。

Xcode: オブジェクトを選択してstackする
Stackするオブジェクトを選択した状態が上の画像です。選択されている状態では、resize handlersが出ているのが分かります。

Canvas右下のstackボタンを押す

この状態で、canvas(editor area)の右下にある「stack」ボタンを押すと、選択されたオブジェクトがひとまとめになるのが分かります。Stackボタンは4つ並んでいるボタンの一番左です。

ポインタをボタンの上に置くと「stack」と表示されます。

Xcode: オブジェクトをstackした後の表示
Stackされたオブジェクトは自動的にレイアウトを調整しますが、調整は縦方向だけです(横方向は調整されません)。上の画像はstack後のXcode。Outline viewを見ると、バラバラだったオブジェクトがstack後はまとまって表示されているのが分かります(画像の「Stack View」と表示されている部分)。

これは多分オブジェクトを縦に並べたので横方向の調整が必要ないんだと思います。もし横に並べると多分横方向のレイアウトが調整されると思います。

補足: 上記画像はXcode 7.3のものです。Xcode 8.0でスタックすると分かりますが、上記のような薄い青色の枠は表示されません。

Stackの「Spacing」を調整

ここでoutline viewから「Stack View」を選択し、attributes inspectorの「Spacing」領域に12を入力してリターンしてみます。Spacingはattributes inspectorの一番上にある項目「Stack View」の中にあります(上の画像参照)。

Xcode: Stackのspacingを調整

結果が上の画像。Spacingの値を増やすと

  • オブジェクト間の縦間隔が伸びる
  • Stack全体の縦の長さが伸びる

ことが分かります。

Pinボタンをクリック

Stackを選択した状態のまま、今度はcanvas右下のボタン「pin」をクリックします。Pinボタンは4つあるボタンの左から3番目です。Pinボタンを押すと下の画像に示したようなメニューが表示されます。

Xcode: Pin menuでconstrainを設定前

Pinメニューからconstraintsをセット

先に加える変更点を書き下しておくと、

  • 上・左・右のconstraintをONにする。具体的には、「spacing to nearest neighbor」より上にある部分で「constraint」(薄い赤色の上下左右に配置されているやつ)をクリックする。クリックすると赤くなります
  • 上のテキストフィールドに60、左右のテキストフィールドに0を入力
  • 下から2番目にある「Update Frames」の横の「None」を「Items of New Constraints」に変更

です。上記変更を加えた後のpinメニュー表示が下の画像のようになります。

Xcode: Pin menuでconstrainを設定後

画像を見比べてもらうと変更した場所が分かると思います。Pinメニューで調整しているのは、選択されているview(今はstackしたオブジェクト群)と他のUI elementとの間隔です。他のUI elementというのは、super view、同じ階層のview(peer viewと言っているモノです)、またmarginも含まれます。今回の例だと、stack view以外にviewがないので、いま設定した間隔は上・左右marginに対するものになります。

Pinメニューの「Add 3 constraints」をクリック

全てのconstraintsの設定が完了したら、一番下のボタン「Add 3 constraints」をクリックします。そうすると、UIの見た目は

Xcode: Pinでconstraintsを設定後のscene表示

このようになるはずです。

左右を見ると分かりやすいと思いますが、間隔を0に設定したのに左右の端から微妙にスペースが空いているのは、「Constrain to margins」にチェックが入っているからです。つまり、marginを一番端っこに設定していることになります。

もし「Constrain to margins」のチェックを外すと、marginではなく画面端に対する間隔を設定することになります。試しに全く同じconstraintsで「Constrain to margins」を外して設定してみると違いが分かります。

テキストフィールドの横幅を調整

テキストフィールドのconstraintsを設定

このままでも悪くないですが、料理名を入力するテキストフィールドの横幅が狭いので広げます。手順はstackにconstraintsをセットした時と同じです。

  • ストーリーボード上で、outline viewからテキストフィールドを選択(Stack Viewを展開)
  • 右下のpinボタンを押してpinメニューを開く
  • 左右のconstraintsを選択、間隔を0にセット
  • Update Framesの右側を「Items of New Constraints」に変更

ここまでセットした状態が

Xcode: テキストフィールドにconstraintsを設定
上の画像のようになります。最後にpinメニューの一番下の「Add 2 constraints」を押して完了。

Size inspectorを開く

テキストフィールドを選択した状態でsize inspectorというメニューをutility areaで開きます。Size inspectorはutility area上にあるボタンの右から2つ目です。

Xcode: テキストフィールドのsize inspector

Size inspectorは名前の通り、選択したオブジェクトのサイズや位置を編集するためのツールです。今変更したいオプションは一番下にある

Intrinsic Size | Default (System Defined)

の部分です。

Intrinsic Sizeで「Placeholder」を選択

Intrinsic Sizeの項目で、「Placeholder」を選択します。

Xcode: Intrinsic sizeをplaceholderに設定

例えばテキストフィールドの大きさ(長さ)は、テキストフィールドに入るコンテンツで決まります。それをintrinsic contents sizeと呼びます。つまり、

intrinsic contents size = コンテンツをviewに表示するのに必要な最小限の大きさ

ということになります。

You can assign UI elements a placeholder intrinsic content size if you need to design a UI for a different size than you can anticipate at design time. Right now, the text field’s only content is its placeholder string, but the actual text a user enters could be longer than that.

という説明の通り、テキストフィールドで必要なintrinsic content sizeは表示されている「料理名を入力」という文字列の長さになります。しかしながら、実際に入力する文字列の長さはそれより長くても構いません。したがって、デフォルトで用意されているよりも長い幅を当てるのが妥当です。

今いちこの設定の効用が理解出来ていません。「Default」と「Placeholder」の両方の設定を試してみましたが、Simulatorでの挙動は変わりませんでした。また、このサンプルではinterface builderでの見た目も変わらなかったので、placeholderにした際の効果が良く分かりませんでした。

Simulatorでここまでの出来を確認

Simulatorを起動して、実機(iPhone 6s Plus)での見た目を確認してみると、

Xcode: Simulatorでfoodtracker appの挙動を確認

こんな感じになっています。また、テキストフィールドからキーボードを使って文字列を入力することも可能です。ここでは試しに「ごはん」と入れてみました。

Resolve Auto Layout Issuesボタンでデバッグ

もしレイアウト周りで何かしら問題が起こった場合は、canvas右下にある4つのボタンの一番右「Resolve Auto Layout Issues」ボタンを押してデバッグすることが出来ます。

今回のケースだと、(私の場合だけかもしれませんが)ラベルのconstraintsを設定した後、

Frame for … will be different for run time

という警告がstack viewとその中身に対して出ました。一応スクリーンショットを貼ると、

Xcode: constraintsをセットした後の警告

このような感じです。「Resolve Auto Layout Issues」ボタンを押して、「Update Frames」でほぼ解決しましたが、なぜかラベルだけはこれで直りませんでした。

上の画像でも表示されていますが、ラベルの警告の中身が

Height will be 200 at run time but is 21 in the canvas

だったので、試しにラベルのintrinsic sizeを「placeholder」にすると、見事にframeの警告が消えました。ただ、これが正しい解決方法なのかどうかは今いちよく分かりません。

上記ラベルの警告はXcode 7.3の場合です。Xcode 8でもstack viewに同様の警告が出ましたが、resolve auto layout issuesボタンで解決しました。

まとめ

ようやくXcodeで基本的なUIを作る事が出来ました。ただ、まともに動いているのはテキストフィールドだけなので、次回UIとコードを繋げてボタンなどが動くようにします。

Part3はこちらから。

「Start Developing iOS Apps (Swift)」でFoodTracker Appを実際に作ってみる その3 [UIとコードを繋ぐ]
公式にある「Start Developing iOS Apps (Swift)」を使って実際にFoodTracker Appを作ってみる、と...
スポンサーリンク
広告1
広告1

シェアする

フォローする