Swift Playgroundsで学ぶiOSプログラミング

縦表示でも横表示でもボタンを中央に固定するプログラミング

文●柴田文彦 編集●吉田ヒロ

2017年06月12日 17時00分

ボタンを配置してユーザーの操作に応答する

 プログラムを起動して何らかの画面を作成しあと、そのまま放置するのではなく何らかのやり取りを実現するため、まずビューコントローラーを用意します。これまでのいろいろな既成のビューを利用する場合も、この点は同じでした。

 そのビューコントローラーの中のviewDidLoadメソッドの中で、基本的な初期設定を実行するのが一般的です。今回はその中でボタンのオブジェクトを配置し、画面全体のビューの背景色を設定し、そのビューにボタンを配置しています。ここまでのプログラムを画面で確認しましょう。

ビューコントローラーのviewDidLoadメソッドの中で、ビューの中央にオレンジ色の「タップして!」というボタンを1つ配置しています。ボタンがタップされるとbuttonTappedメソッドを呼び出します

 この中には、今回の重点ポイントとして挙げた要素が2つ出てきています。1つは、ボタンをその上に配置する親のビューのサイズを読み取って、ちょうど真ん中になるようにボタンを配置している部分。もう1つは、ユーザーがボタンをタップする操作に応答するために「アクション」を設定している部分です。

 まずボタンの配置ですが、このために、まず親となるビュー(self.view)のboundsというプロパティを調べます。それによってビューの幅と高さがわかります。ボタンは、左上の角のX座標が、ビューの幅の4分の1、Y座標がビューの高さの7分の3の位置になるように、またボタンの幅はビューの幅の2分の1、高さはビューの高さの7分の1になるように設定しています。言葉だけではわかりにくいにで図で確認してみましょう。

ボタンの大きさは、ビューの2分の1の幅、7分の1の高さに設定し、その左上の角がビューの幅の4分の1、高さの7分の3の座標位置にくるようにすることで中央に配置します

 これで横長のボタンがビューの中央に配置されるはずです。実はこれは結果的には失敗だったことがわかるのですが、この時点では正しい設定のように思えます。

 ユーザーがボタンを操作した際の応答のために、ボタンのaddTargetメソッドを利用してアクションを設定しています。これは、特定の操作に対して、どのメソッドを呼び出すのかを指定するものです。ここでは同じビューコントローラーの中で記述するbuttonTappedというメソッドを、touchUpInsideというイベントが発生したら呼び出すように設定しています。このtouchUpInsideというのは、ボタンにタッチした指がボタンの内側で離されたというもので、ボタンのタップを検出するためのごく一般的なイベントです。

ボタンがタップされたらボタンの色をランダムに変更する

 次にボタンのイベントを処理するためターゲットとして指定したアクションのメソッドbuttonTappedを見ておきましょう。実行結果とともに画面で示します。

同じビューコントローラーの中に、ボタン操作に対するアクションとなるbuttonTappedメソッドを記述します。この中ではボタン自身の色をランダムに設定するコードだけを記述します。これを動かすとボタンの位置と大きさは変ですが、タップに反応してボタンの色がランダムに変わるはずです

 この中で実行しているのはただ1つ、ボタンの背景色を設定する処理だけです。ここで今回のもう1つのポイント、乱数によってランダムな色を作成する処理が出てきます。

 これまで、色の設定は省力化のために既定の色の名前を使うことがほとんどでした。それではランダムに設定するのが難しいので、ここではUIColorというクラスのオブジェクトを作成する際にred、green、blue、alphaの4つの要素を別々に数値で指定しています。このうちalphaには完全な不透明を表す1.0を設定していますが、残りの要素は乱数によって決めています。

 ここで乱数を得るために使っているのがarc4random_uniformというファンクションです。これは、0から引数で指定した値よりも1だけ小さい整数をランダムに返します。ここではいずれも1000を指定しているので、0~999までの整数を返すことになります。それを999.0で割ることで、結局0.0から1.0の間の実数が得られます。それをCGFloat型に変換してから、red、gree、blueの各要素として指定しています。

 このプログラムを実行すると、すでに同じ図に結果が見えているように薄いグレーのビューの上にオレンジ色のボタンが配置された画面が表示されます。ボタンの位置と大きさは狙った結果とは異なりますが、とりあえずそれには気づかなかったことにして、ボタンをタップしてみましょう。そのたびにボタンの色がランダムに変化するはずです。

変化するビューのサイズに応じて適宜ボタンのレイアウトを変更する

 ここまでのプログラムによって配置されたボタンは、狙った位置(ビューの中央)から右下に大きくずれてしまっています。ビューの大きさを調べて真ん中にくるように配置したはずなのに、どうしてこうなってしまったのでしょうか。それを調べるために、Swift Playgroundsのデバッグ機能を使っていきます。

 まずviewDidLoadメソッドの中で、取得したビューのサイズに関する情報を確認しましょう。定数vSizeにself.view.boundsを代入している行の右側に表示される長方形をタップすると、その長方形の左上の角の座標と幅、高さがわかります。

ビューが最初にロードされた直後に呼び出されるviewDidLoadメソッドの中では、ビューのサイズは1024×768ポイントになっていることがわかりました

 ここに表示される数字は高さが768、幅は1024というものでした。これらの単位は「ポイント」で、iOSデバイスの仮想的な座標空間を表すものです。例えばiPadの場合、Retinaディスプレイの物理的な解像度は2048×1536ピクセルですが、ポイントサイズで言えば、それぞれ半分の1024×768ポイントとなっています。初期のiPadはピクセル=ポイントだったのですが、Retina以降は2ピクセル=1ポイントとなっているのです。ポイントは物理的な解像度が異なっても、プログラムからは一様な座標として扱えるようにするためのクッションのようなものです。

 ここまで説明すればお気付きでしょう。このviewDidLoadメソッドの中で調べたビューの大きさは、iPadを横向きに持った際の画面全体のサイズを表しています。通常のアプリであれば画面全体が使えるので、この数字をそのまま使っても問題ない場合が多いのです。このメソッドの最下行で、ボタンをビューに追加している部分の右側に表示される長方形をタップしてみましょう。

viewDidLoadメソッドの中でビューにボタンを配置した直後の状態を調べると、横長のグレーのビューの中央にオレンジ色のボタンが配置されていることが確認できます

 すると、横長のグレーの画面の中央にオレンジのボタンが配置されたイメージが表示されます。これを見ると少なくともこの時点、つまりビューコントローラーにビューがロードされた時点では、プログラムは期待したとおりに動いていることが確認できます。

 それならば、どの時点で期待が裏切られるのでしょうか。Swift Playgroundsの中のプログラムは、iPadの画面全体ではなく、画面の右半分、さらに周りに余白のある領域をアプリ画面として使います。そのため、ビューコントローラーのビューは、どこかの時点でプレイグラウンドに合わせた小さな縦長の画面(iPadを横向きに持った場合)に合わせたサイズに変更されてしまうのです。そのままではボタンはビューからはみ出してしまいます。それを防ぐには、ビューのサイズが変更されたあとでボタンをレイアウトし直す必要があるのです。

 試しに、ボタンのタップによって呼び出されるbuttonTappedメソッドの中に1行を追加して、親ビューの大きさを確認してみましょう。

ボタンがタップされると呼び出されるbuttonTappedメソッドに1行を追加して、その時点でのビューの大きさを調べてみると、当然ながらすでに縦長の小さなサイズに変更されています

 すると、ここでは高さが569、幅は432ポイントに変更されていることがわかります。これではボタンが右下にずれるはずです。ただし、これはボタンがタップされたあとなのでボタンのレイアウトをし直すには遅すぎます。実はビューコントローラーの既定のメソッドの中には、これからビューがサブビュー(ここではボタン)をレイアウトすることを通知するためのものが用意されています。それはviewWillLayoutSubviewsというものです。この中身を記述しておくことによって、そのタイミングで所望の動作を実行することが可能となります。

 この例では、上に示したviewDidLoadメソッドの中で実行していたビューのサイズの取得と、それに合わせたボタンの位置、大きさの指定を、そのままviewWillLayoutSubviewsメソッドに中に移動しました。画面で確認してください。

ビューのサイズを取得してそれに合わせてボタンのサイズと位置を決める処理を、viewDidLoadメソッドからそっくりviewWillLayoutSubviewsメソッドに移動します

 この変更後にプログラムを起動すると、オレンジ色の「タップして!」ボタンは当初の期待どおり画面の中央にレイアウトされます。

変更後のプログラムを起動すると、初期状態でオレンジ色のボタンは、狙った通り、プレイグラウンド画面の中央に配置されます。タップすれば色が変わる動作はもちろんそのままです

 さらに、そのままiPadの画面の縦長に変更しても画面がレイアウトし直されるので、こんどは横長のビューの中央にやはりビューの4分の1幅、7分の1の高さでボタンが配置されるようになります。

iPadの画面を縦長に持ち替えても、ボタンはビューの中央に狙った通りにレイアウトし直されます。画面サイズの異なる12.9インチモデルでも適切にレイアウトされます

 念のために、viewWillLayoutSubviewsメソッドが呼び出された際のビューのサイズを確認しておきましょう。サイズを調べている行の右側を見ると、このメソッドが連続して複数回呼び出されていることがわかります。タップして値を表示させてみると、ビューサイズの変遷がわかります。

viewWillLayoutSubviewsメソッドは、プログラムを起動後に連続して複数回呼び出されます。その間にビューのサイズが変化し、起動後もユーザーの操作に応じて変化する様子がわかります

 iPadの画面を縦横に持ち替えた場合に、レイアウトし直されるだけでなく、ソースコードの編集状態でツールバーが表示されます。プレイグラウンド画面に触れてツールバーが表示されなくなった場合にも、ビューのサイズは微妙に変化しています。いずれにしてもviewWillLayoutSubviewsメソッドを利用することで、ビュー内のレイアウトを適切に維持できることがわかりました。実際のiOSアプリでは、まったく別の手法で画面レイアウトをフレキシブルなものに保つことが主流ですが、少なくともSwift Playgroundsの場合には今回示した手法の方が有効のように思われます。

次回の予定

 今回からSwiftによるiOSプログラミングの基本に戻るということで進めてきましたが、画面の見かけから想像するよりは内容は濃かったのではないかと思います。次回以降もしばらくはこの路線で進みます。今回はボタンだけでしたが、次回からはスイッチやスライダーなどのほかのコントロール類も取り上げて、それらが発生するイベントに対処する方法を考えていくつもりです。

■今回作ったプログラム

Swift_41-1
Swift_41-2

mobileASCII.jp TOPページへ