「はじめて道場・裏道場に入門する」というあなた,「道場・裏道場についていくのがシンドイ」というあなた,このページにある一連の自主トレでじっくり基礎体力をつくってください.初心者でも必ず出来るようにしてあります.まずは手を動かしてみることが大切です.
プログラムを書かないでアプリを開発します.ほとんど「裏道場」の 1A と同じ課題です.右図のような画面を表示するだけのシンプルなアプリです.
- Xcode プロジェクトの新規作成:Xcode を起動し,Create a new Xcode project を選びます.左側リストの iPhone OS の Application を選択し,右側から Window-based Application を選択し,新しいプロジェクトを作成します(参考画面).プロジェクトの名前はたとえば PreDojo1 としましょう.
- Interface Builder の起動:Xcode ウィンドウの左側にあるリストから Resources をダブルクリックして展開し,その中にある MainWindow.xib をダブルクリックします(参考画面).これで Interface Builder が起動し,いくつかの新しいウィンドウが開きます.
- 画面デザイン(準備):ウィンドウ Window が開いていると思います(参考画面).もし見つからなかったら,ウィンドウ MainWindow.xib から項目 Window をダブルクリックして開きます(参考画面).つぎに,ウィンドウ Library を開きます(参考画面).すでに開かれているしれませんが,そうでない場合はメニューバーから Tools > Library を選択します.
- 画面デザイン(配置):Library の Objects の中から Label を見つけます(参考画面).Label の項目をドラッグし,Window の上(やや上部)にドロップします.同じように,Image View をその下に(やや上下間隔を空けて)ドロップします.これら要素を Window 上でドラッグして位置を調整し,またマウスで引き延ばすことでサイズを調整してください(参考画面).
- 画面デザイン(調整):ウィンドウ Inspector を開きます(参考画面).すでに開かれているしれませんが,そうでない場合はメニューバーから Tools > Inspector を選択します.この Inspector 上部の左端にあるアイコンを選択し,要素ごとの属性(Attribute)を調整していきます.
- 画面デザイン(Label の設定):まず,Window の Label 要素を選択し,この Inspector 上でその Text 属性を "道場へようこそ" のように入力し,Font 属性をやや大きめのサイズに,また配置は中央寄せに設定してください.必要に応じて,Window 上での Label 要素の大きさや位置を適当に調整してください(参考画面).
- 画面デザイン(Image View の設定登録):つぎに,宮城大学のシンボルマークの画像ファイルをダウンロードして,そのファイルを Xcode の Resources にドロップしてください.ドロップしたときのダイアログで "Copy ..." のチェックを入れるとよいです.つづいて,ウィンドウ Window の画面要素 Image View 要素を選択し,Inspector 上で Image 属性としてこの画像ファイルを指定します.また Mode 属性を Aspect Fit に,Background 属性をWhite に設定します.必要に応じて,Window 上での Image View 要素の大きさや位置を適当に調整してください(参考画面).
- 画面デザイン(保存):Interface Builder での画面デザインや各要素の設定値を有効にするために File > Save します.Interface Builder での作業を Xcode でのアプリケーションのビルドに反映させるには,必ず Save する必要があります.(逆に Xcode での作業を Interface Builder に反映させるには,Xcode で Save する必要があります.)
- アプリケーションのビルドと実行:Xcode のウィンドウに戻り,Build and Run を実行します.iPhone Simulator が起動し,冒頭にあげたような画面が出るはずです.
解説:Xcode はプログラミングに,Interface Builder は画面デザインに使います.Xcode でプロジェクトを新規作成すると,アプリ動作に最低限必要なプログラムが自動的に生成されます.要は,空っぽの Window が開くだけのアプリです.ここでは Interface Builder を使って,この Window に,ラベル(UILabel クラスのインスタンス)と画像(UIImageView クラスのインスタンス)を貼り付けています.
発展:Window の背景色を変えてみてください.ラベルの文字色を変えてみてください.画面要素の位置や大きさは,Window 上でマウスを使って調整することができますが,Interface Builder 上の Inspector を使うことで,より細かい調整が可能です.いろいろやってみてください.
上のアプリケーションにボタンを追加します.ボタンに反応してラベルのテキストが変わるように,プログラムを書いて実装していきます.
- 画面デザイン(ボタン追加):Interface Builder 上で,Label 要素と Image View 要素の間に2つのボタンを追加します.Library から Round Rect Button をドラッグし Window にドロップして.適当に大きさと位置を調整してください.(参考画面).
- プログラム(コントローラのクラス定義):Xcode 上で,左端リストの Classes 選択した状態で,メニューバーから File > New File... を選び,iPhone OS > Cocoa Touch Class > Objective-C class を作成します.ファイル名は Controller とします.これで Controller.m と Controller.h が生成されます.これがボタン応答をつくりだすコントローラとなります.
- プログラム(.h ファイルの編集):ヘッダファイル Controller.h をダブルクリックし,つぎのように編集し,保存します.赤字はテンプレートから書き加えた部分です.IBOutlet で始まる3行は,インスタンス変数の宣言部分です.いずれも画面要素(オブジェクト)へのポインタであり,コントローラからのアウトレット(出力端子)となります.また,- (IBAction) で始まる2行は,メソッドのプロトタイプ宣言で,ボタンを押したときに呼び出されるメソッドになります.
Controller.h(参考画像)
#import <Foundation/Foundation.h> @interface Controller : NSObject { IBOutlet UILabel *titleLabel; IBOutlet UIButton *enterButton, *leaveButton; IBOutlet UIImageView *myuImage; } - (IBAction)enterDojo; - (IBAction)leaveDojo; @end
- プログラム(.m ファイルの編集):インプリメンテーションファイル Controller.m をつぎのように編集し,保存します.赤字はテンプレートから書き加えた部分です.ヘッダファイル(Controller.h)でプロトタイプ宣言したメソッドを実装しています.ラベル要素(titleLabel)の表示文字列(text)を,初期状態の「道場へようこそ」から別のものに差し替える動作を担当します.
Controller.m(参考画像)
#import "Controller.h" @implementation Controller - (IBAction)enterDojo { titleLabel.text = @"よろしくお願いします!"; } - (IBAction)leaveDojo { titleLabel.text = @"まあ,そう言わず,ぜひ!"; } @end
- プログラム(コントローラのオブジェクト生成):つぎに,上で定義した Controller クラスのオブジェクト(インスタンス)を生成します.Interface Builder で,ウィンドウ Library を表示し,その上端から Classes を選択します.クラスの一覧から Controller を見つけて(参考画面),それをウィンドウ MainWindow.xib にドラッグ&ドロップしてください(参考画面).これで,Controller クラスのインスタンスが1つ生成されました.
- プログラム(コントローラを画面に接続):ウィンドウ MainWindow.xib の Controller オブジェクトを選択し,Inspector を開きます(参考画面).左から2番目(矢印)の項目を選び,Outlet の4項目を Window 上の画面要素に接続します.具体的には,Outlet の各項目について,その右端にある丸印から Control+クリックし,Window 上の対応要素までドラッグします (参考画面).これで,プログラム(Controller)から画面要素を「操作」することが可能になります.
- プログラム(画面をコントローラに接続):上と同様に,Received Action の2項目をその発信源となる2つのボタンに接続します.そのとき監視イベントとして Touch Up Inside を選びます(参考画面).これで,ユーザが画面要素(ボタンなど)を押したとき,指定したメソッドが呼び出されるようになります.Interface Builder での一連の作業が終わったら,File > Save してください.
- アプリケーションのビルドと実行:Xcode のウィンドウに戻り,Build and Run を実行します.iPhone Simulator が起動し,冒頭にあげたような画面が出ます.ボタンを押して,動作を確かめてください.
解説:ここでは Controller というクラスを Xcode 上で作成し,そのインスタンスを Interface Builder 上で生成するようにしました.MainWindow.xib には,Controller(制御プログラム)と Window(画面)が入っています.どちらもアプリ起動時にメモリ上に展開され,実行されます.Controller には,出力端子(IBOutlet)と入力端子(IBAction)があり,どちらも Window 上の画面要素に接続されます.前者は,プログラムから画面要素を操作するためのものであり,後者は,画面要素が受け取ったイベント(Touch Up Inside など)をプログラムに伝えるためのものです.
発展1:ボタンに応答するメソッド(-enterDojo, -leaveDojo)では,titleLabel の text 属性を差し替えています.これを,たとえばつぎのように拡張してみてください.
- (IBAction)enterDojo { titleLabel.text = @"よろしくお願いします!"; titleLabel.textColor = [UIColor blackColor]; myuImage.hidden = NO; } - (IBAction)leaveDojo { titleLabel.text = @"まあ,そう言わず,入門を!"; titleLabel.textColor = [UIColor redColor]; myuImage.hidden = YES; }
どのような属性があるかを調べるには,Xcode > Help > Developer Documentation を開き,右上にある検索窓に,たとえば UILabel と入力してください.左端のメニューから Properties をクリックすると,属性の一覧とその説明が(英語ですが)表示されます.また,検索窓に UIColor と入力し,左端のメニューから Class Methods をクリックすれば,どのような「色」を設定できるのかがわかります.いろいろ試してみてください.
発展2:iPhone アプリは,必ず1つの UIWindow オブジェクトをもち,これを背景として,さまざまな画面要素が配置されます.それでは,入門を断られたとき,この背景全面を赤にするにはどうすればよいでしょうか.まず,Controller.h をつぎのように編集し,インスタンス変数に window を加え,これを背景となる UIWindow オブジェクトへのアウトレットとします.
...
@interface Controller : NSObject {
IBOutlet UILabel *titleLabel;
IBOutlet UIButton *enterButton, *leaveButton;
IBOutlet UIImageView *myuImage;
IBOutlet UIWindow *window;
}
...
また,対応する Controller.m をつぎのように編集し,ボタンが押されたときに window の backgroundColor 属性を差し替えるようにします.
... - (IBAction)enterDojo { titleLabel.text = @"よろしくお願いします!"; myuImage.hidden = NO; window.backgroundColor = [UIColor whiteColor]; } - (IBAction)leaveDojo { titleLabel.text = @"まあ,そう言わず,入門を!"; myuImage.hidden = YES; window.backgroundColor = [UIColor redColor]; } ...
最後に,Interface Builder の Inspector を使って,Controller の window アウトレットを,Window 上の背景部分(UIWindow)に接続するようにします.これをビルドし実行すれば,「やめておく」を押したとき,背景全面が真っ赤になるはずです.「入門する」を押せば元の白に戻ります.やってみてください.
カラフルなアプリをつくります.私たちが感じる色は3次元の数値で表現することができ,そのための表色系として XYZ, RGB, HSV などが知られています.ここでは HSV 表色系を取り上げ,それを構成する3つのパラメタ,つまり色相(H: hue)・彩度(S: saturation)・明度(V: value)を独立に操作することで,画面の背景色を変化させてみます.(右図参照)
- Xcode プロジェクトの新規作成:今回は View-based Application のプロジェクトを新規作成します.プロジェクトの名前はたとえば PreDojo2 としましょう.
- 画面デザイン:Resources > PreDojo2ViewController.xib をダブルクリックして Interface Builder を起動し,Library から View 上へ,Label や Slider を配置します.Label は View 上端付近・横幅一杯に配置し,やや大きめのフォント(24pt)でセンタリング配置に設定します.Slider は Window 下端付近・横幅一杯に3つ配置します.値の範囲はデフォルト(0.0〜1.0)のままで OK です.最後に Save してください.(参考画面)
- プログラム(コントローラのクラス定義):Classes の中に PreDojo2ViewController.h と PreDojo2ViewController.m が既に用意されているはずです.まず,ヘッダファイル Predojo2ViewController.h をつぎのように編集し,保存します.赤字はテンプレートから書き加えた部分です.
#import <UIKit/UIKit.h> @interface PreDojo2ViewController : UIViewController { IBOutlet UILabel *label; IBOutlet UISlider *sliderH, *sliderS, *sliderV; } - (IBAction)updateHSV; @end
#import "PreDojo2ViewController.h" @implementation PreDojo2ViewController - (IBAction)updateHSV { float valH = [sliderH value], valS = [sliderS value], valV = [sliderV value]; label.text = [NSString stringWithFormat:@"H=%.2f S=%.2f V=%.2f", valH, valS, valV ]; self.view.backgroundColor = [UIColor colorWithHue:valH saturation:valS brightness:valV alpha:1.0 ]; } ...
- プログラム(コントローラを画面に接続):ウィンドウ PreDojo2ViewController.xib の File's Owner を選択し,Inspector を開きます.左から2番目(矢印)の項目を選び,Outlet の5項目を View 上の画面要素に接続します.詳しくは【2. Hello PreDojo (Part II)】のステップ6を参考にしてください.なお,3つのスライダは上から H, S, V の次元に対応します.(参考画面)
- プログラム(画面をコントローラに接続):上と同様に,Received Action の updateHSV 項目をその発信源となる3つのスライダに接続します.監視イベントとしては Value Changed を選んでください.1つの updateHSV 項目から3つの接続(Multiple 接続)が作られていることを,Multiple を(三角ボタンを押して)展開して,確認してください.(参考画面).これで,ユーザがいずれかのスライダを操作したとき,メソッド updateHSV が呼び出されるようになります.Interface Builder での一連の作業が終わったら,File > Save してください.
- アプリケーションのビルドと実行:Xcode のウィンドウに戻り,Build and Run を実行します.iPhone Simulator が起動し,冒頭にあげたような画面が出ます.スライダを操作して,動作を確かめてください.
解説:いずれかのスライダが操作されたとき,共通のメソッド updateHSV が呼び出されます.updateHSV は,各スライダの値を読み出し,それら3つの値(色相・彩度・明度)から色情報(UIColor オブジェクト)を生成し,それを View(self.view)の背景色(backgroundColor)としています.スライダ(UISlider)は value というインスタンス変数があり,現在値の読み出しだけでなく,新しい値の書き込み(スライダが動く)も可能です.なお,色情報は RGB 表色系での指定(-colorWithRed:green:blue:alpha:)も可能です.alpha は透明度(0.0 で完全透明,1.0 で完全不透明)を表わします.
発展1:アプリを起動した直後は,label には "Label" と表示され,また View の背景色はデフォルトの灰色のままです.これを起動時に初期化できるとよいでしょう.そこで,PreDojo2ViewController.m の中でコメントアウトされている -viewDidLoad を有効にし,その中身を書き換えます.このメソッドは,PreDojo2ViewController.xib が読み込まれ,View が用意できたとき,自動的に呼び出されます.
- (void)viewDidLoad {
[super viewDidLoad];
[self updateHSV];
}
発展2:label の文字色は黒になっていますが,これだと背景が暗い色になったとき見づらいでしょう.たとえば,背景色の明度(V)が 0.5 以上の場合は黒,0.5 未満の場合は白のように指定してみてください.以下は参考コードです.
if (valV > 0.5)
label.textColor = [UIColor blackColor];
else
label.textColor = [UIColor whiteColor];
label.text = [NSString stringWithFormat:@"H=%.2f S=%.2f V=%.2f",
valH, valS, valV ];
上のアプリケーションにタッチ機能を加えます.タッチした座標に応じて,色相(H)と彩度(S)を変化させます.明度(V)については,従来どおり,画面下端のスライダで制御することにします.
- タッチ応答機能の追加:View がユーザからのタッチを感知したときに呼び出されるメソッドを PreDojo2ViewController.m に加えます.View Controller は,管理対象となる View 上で (1) 指が置かれたとき,(2) ドラッグされたとき,(3) 指が離れたとき,(4) 何らかの要因でタッチ監視がキャンセルされたときに呼び出されるメソッドが決まっています.ここでは,(1) と (2) のメソッドを,つぎのように PreDojo2ViewController.m に書き加えます.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self touchesMoved:touches withEvent:event]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self.view]; CGRect bounds = [self.view bounds]; sliderH.value = point.x / bounds.size.width; sliderS.value = point.y / bounds.size.height; [self updateHSV]; }
- アプリケーションのビルドと実行:Build and Run を実行します.iPhone Simulator が起動し,前出のアプリと同じ画面が出ます.画面をタッチ(マウスでクリックあるいはドラッグ)してみてください.スライダが動き,背景色が変化します.
解説1:-touchesBegan は指が置かれたときに呼び出されるメソッドです.-touchesMoved はドラッグされたときに呼び出されるメソッドです.どちらも,タッチされた x-y 座標を読み取り,それを View の縦横の長さで割ることで,0.0〜1.0 の数値を色相と彩度のスライダに設定しています.その後,-updateHSV を呼び出すことで,View の背景色と Label の表示内容を更新しています.
解説2:このほかに,-touchesEnded と -touchesCancelled というメソッド呼び出しもあります.前者は指が画面から離れたときに呼び出され,後者は何らかの要因(電話がかかってきたなど)によってタッチ監視がキャンセルされたときに呼び出されます.
発展:Label をもう一つ用意し,そこにタッチされた x-y 座標を表示するようにしてください.たとえば,右図のような画面になります.-touchesMoved の内部で使われている変数 point.x, point.y が,タッチ位置の x 位置と y 位置を保持しています.ただし,View の内部(bounds)での位置です.したがって,ステータスバーの直下が y=0 となっているはずです.確かめてください.
こんどはマルチタッチ(2本以上の指で画面をタッチすること)によって,色相・彩度だけでなく明度についてもタッチだけで操作することを試みます.人差し指と親指で画面をタッチし,その距離を拡げたり縮めたりすること(ピンチ)で,明度を増減できるようにします.Part II で利用した -touchesBegan, -touchesMoved ではなく,ここでは UIGestureRecognizer を利用して,ユーザのタッチ(マルチタッチ)を自動認識するようにします.
- ジェスチャ自動認識の仕込み:まず,PreDojo2ViewController.m から,-touchesBegan と -touchesMoved のメソッドを削除します.つぎに,パン(シングルタッチでドラッギング)とピンチ(ダブルタッチで拡大縮小)のジェスチャを自動認識するように,下記コードのように -viewDidLoad に設定します.これで,パンが自動認識されると -touchesPan が呼び出され,ピンチが自動認識されると -touchesPinch が呼び出されるようになります.
- (void)viewDidLoad { [super viewDidLoad]; UIPanGestureRecognizer *recPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(touchesPan:) ]; [self.view addGestureRecognizer:recPan]; [recPan release]; UIPinchGestureRecognizer *recPinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(touchesPinch:) ]; [self.view addGestureRecognizer:recPinch]; [recPinch release]; [self updateHSV]; }
- ジェスチャ応答の仕込み:ジェスチャが自動認識されたときに呼び出されるように設定したメソッド(-touchesPan, -touchesPinch)を実装します.まず,PreDojo2ViewController.h に新しいインスタンス変数 baseDist, baseValue を加えます.これらはピンチによって明度に相対変化を与えるために使います.つぎに,PreDojo2ViewController.m に -touchesPan, -touchesPinch を実装します.前者はシングルタッチ時のみ応答し,後者はダブルタッチ時のみに応答するようにします.
#import <UIKit/UIKit.h> @interface PreDojo2ViewController : UIViewController { IBOutlet UILabel *label; IBOutlet UISlider *sliderH, *sliderS, *sliderV; float baseDist, baseValue; } - (IBAction)updateHSV; @end
- (void)touchesPan:(UIPanGestureRecognizer *)recPan { if ([recPan numberOfTouches] == 1) { CGPoint point = [recPan locationOfTouch:0 inView:self.view]; CGRect bounds = [self.view bounds]; sliderH.value = point.x / bounds.size.width; sliderS.value = point.y / bounds.size.height; [self updateHSV]; } } - (void)touchesPinch:(UIPinchGestureRecognizer *)recPinch { if ([recPinch numberOfTouches] == 2) { CGPoint point0 = [recPinch locationOfTouch:0 inView:self.view], point1 = [recPinch locationOfTouch:1 inView:self.view]; float difX = point0.x - point1.x, difY = point0.y - point1.y, dist = sqrt(difX * difX + difY * difY); if (recPinch.state == UIGestureRecognizerStateBegan) { baseDist = dist; baseValue = sliderV.value; } float value = baseValue + (dist - baseDist) / 1000.0; if (value < 0.0) value = 0.0; else if (value > 1.0) value = 1.0; sliderV.value = value; [self updateHSV]; } }
- アプリケーションのビルドと実行:Build and Run を実行します.画面をシングルタッチ/ドラッグすると,Part II のときと同じように背景色が変化します.つぎに,画面を2本の指(人差し指と親指)でピンチ動作すれば,明度(V)が増減します.シミュレータでは Option キーを押しながらマウスドラッグすることでピンチ動作が可能です.やってみてください.
解説1:ジェスチャ自動認識は,UIGestureRecognizer のサブクラス(UIPanGestureRecognizer など)のインスタンスを生成し,それを view(ここでは self.view)に設定する(-addGestureRecognizer)ことで可能になります.認識できるジェスチャには,Tap, Pinch, Rotation, Swipe, Pan, LongPress があり,ひとつの view に複数設定することができます.対象となるジェスチャが認識されると,あらかじめ指定しておいたターゲット(ここでは self)のアクション(ここでは @selector(touchesPinch:) など)が呼び出されます.このアクションによって呼び出されるメソッドに,ジェスチャへの応答をプログラミングしていきます.詳しくは,UIGestureRecognizer のヘルプ(Help > Developer Documentation > Search)をご覧ください.
解説2:ピンチ動作への応答部分(-touchesPinch)では,まず2本の指のあいだの距離 dist を計算します.ピンチ動作の開始時(UIGestureRecognizerStateBagain)に,この距離 dist を baseDist に記録し,また明度スライダの値 sliderV.value を baseValue に記録しておきます.ピンチ動作が続くあいだ,baseValue を基準として,距離 dist と baseDist の差(=相対的なピンチ動作)に応じた変位を与えて,明度(V)として設定しています.
発展1:画面をタップしただけでは色が変化しません.これを改善してください.UITapGestureRecognizer を追加し,呼び出されるメソッドとして -touchesTap を用意します.その中身は -touchesPan とほぼ同じです.
- (void)touchesTap:(UITapGestureRecognizer *)recTap { if ([recTap numberOfTouches] == 1) { CGPoint point = [recTap locationOfTouch:0 inView:self.view]; CGRect bounds = [self.view bounds]; sliderH.value = point.x / bounds.size.width; sliderS.value = point.y / bounds.size.height; [self updateHSV]; } } - (void)viewDidLoad { ... UITapGestureRecognizer *recTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchesTap:) ]; [self.view addGestureRecognizer:recTap]; [recTap release]; ... }
発展2:ここまで完成したら,もうスライダは必要ないですね.スライダを画面から削除して,インスタンス変数 sliderX を float 型の変数 valX と入れ替えます.各メソッド定義に現われる sliderX.value を valX に書き換えます.また -viewDidLoad で valX の初期値をセットするとよいでしょう.
...
@interface PreDojo2ViewController : UIViewController {
IBOutlet UILabel *label;
float valH, valS, valV;
float baseDist, baseValue;
}
...
... - (IBAction)updateHSV { if (valV > 0.5) label.textColor = [UIColor blackColor]; else label.textColor = [UIColor whiteColor]; label.text = [NSString stringWithFormat:@"H=%.2f S=%.2f V=%.2f", valH, valS, valV ]; self.view.backgroundColor = [UIColor colorWithHue:valH saturation:valS brightness:valV alpha:1.0 ]; } ... (以下同様) ... - (void)viewDidLoad { ... valH = 0.5; valS = 0.5; valV = 0.5; [self updateHSV]; }
バスや電車の時刻表などをすばやく見るためのブラウザをつくります.おたのしみに.