一般公開「iPhone SDKではじめるサンデープログラミング:中野洋一」
(MOSADeN Onlineでは、掲載日から180日を経過した記事を一般公開しています。)- iPhone SDKではじめるサンデープログラミング (34)(2011/07/11:掲載)
- iPhone SDKではじめるサンデープログラミング (33)(2011/06/08:掲載)
- iPhone SDKではじめるサンデープログラミング (32)(2011/05/25:掲載)
- iPhone SDKではじめるサンデープログラミング (31)(2011/05/11:掲載)
- iPhone SDKではじめるサンデープログラミング (30)(2011/04/26:掲載)
- iPhone SDKではじめるサンデープログラミング (29)(2011/04/12:掲載)
- iPhone SDKではじめるサンデープログラミング (28)(2011/03/29:掲載)
- iPhone SDKではじめるサンデープログラミング (27)(2011/02/09:掲載)
- iPhone SDKではじめるサンデープログラミング (26)(2011/01/19:掲載)
- iPhoneではじめるサンデープログラミング (25)(2010/09/14:掲載)
- iPhoneではじめるサンデープログラミング (24)(2010/08/27:掲載)
- iPhoneではじめるサンデープログラミング (23)(2010/08/11:掲載)
- iPhoneではじめるサンデープログラミング (22)(2010/07/20:掲載)
- iPhoneではじめるサンデープログラミング (21)(2010/07/13:掲載)
- iPhoneではじめるサンデープログラミング (20)(2010/06/29:掲載)
- iPhoneではじめるサンデープログラミング (19)(2010/06/08:掲載)
- iPhoneではじめるサンデープログラミング (18)(2010/05/26:掲載)
- iPhoneではじめるサンデープログラミング (17)(2010/05/11:掲載)
- iPhoneではじめるサンデープログラミング (16)(2010/04/20:掲載)
- iPhoneではじめるサンデープログラミング (15)(2010/04/06:掲載)
- iPhoneではじめるサンデープログラミング (14)(2010/03/23:掲載)
- iPhoneではじめるサンデープログラミング (13)(2010/03/09:掲載)
- iPhoneではじめるサンデープログラミング (12)(2010/02/23:掲載)
- iPhone SDKではじめるサンデープログラミング (11)(2010/02/09:掲載)
- iPhone SDKではじめるサンデープログラミング (10)(2010/01/26:掲載)
- iPhone SDKではじめるサンデープログラミング (9)(2010/01/12:掲載)
- iPhone SDKではじめるサンデープログラミング (8)(2009/12/22:掲載)
- iPhone SDKではじめるサンデープログラミング (7)(2009/12/08:掲載)
- iPhone SDKではじめるサンデープログラミング (6)(2009/11/24:掲載)
- iPhone SDKではじめるサンデープログラミング (5)(2009/11/10:掲載)
-
iPhone SDKではじめるサンデープログラミング (34)
この記事は、2011年07月11日に掲載されました。前回からずいぶんと間が空いてしまいました。すみません。
この原稿を書いている現在、Mac OS X Lionのリリースが来週と噂されています。
今からとても楽しみですが、それ以上にこの秋リリース予定のiOS 5に筆者は注目しています。
iOS 5には200を超える新機能が追加されており、アプリ側からも利用できる機能も多いので、さらにiOSアプリの市場が盛り上がることでしょう。
その新機能の中にはTwitterとの連携も含まれているので、このシリーズで組み立てたプログラムは、Appleから提供されるフレームワークによって実装可能になるはずです。
これまではオープンソースに頼っていたわけですが、Appleが正式にTwitterをサポートしてくれるのは、プログラマの負担を考えると大変ありがたいことです。
この秋以降、Twitterの機能を取り込んだアプリがたくさん登場することでしょう。ではこのシリーズの締めくくりとして、アプリを仕上げたいと思います。
一応Twitterクライアントとして動く最低限のアプリとして出来上がりましたが、いくつか気になる問題があります。
- 1. ツイート画面のキーボードが隠れない。
2. ツイート時の文字数をチェックしていない。
3. タイムラインの更新時にアラートパネルが表示される。
これらを解決してこのシリーズを終えたいと思います。ツイート画面のキーボードが隠れない
ツイート画面においてキーボードが表示されると、画面下部のタブバーが隠れてしまい、他の画面に切り替えることが出来ません。
そこで、ツイートした後にキーボードを隠すためのプログラムを、TweetViewController.mのtweetButtonPushedメソッドに追加します。
tweetTextViewに対して、resignFirstResponderメソッドを実行すると、キーボードが隠れます。- (IBAction)tweetButtonPushed { SimpleClientAppDelegate *app = (SimpleClientAppDelegate*)[[UIApplication sharedApplication] delegate]; [app.model tweet:tweetTextView.text]; [tweetTextView resignFirstResponder]; // 追加行 }
ツイート時の文字数をチェックしていない
Twitterの制限である140文字を超えても、サーバにメッセージが送信されてしまいます。
送信前に文字数をチェックして、140文字を超える場合はアラートを表示させましょう。
こちらも、TweetViewController.mのtweetButtonPushedメソッドに追加します。
tweetTextViewのtextプロパティは入力されたテキストを意味しますので、lengthプロパティから文字数を得ることができます。
lengthが140を超えるときに、アラートを表示されてメソッドを抜けます。- (IBAction)tweetButtonPushed { // ここから追加 if ( tweetTextView.text.length > 140 ) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"The message exceeds 140 characters." delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; [alertView show]; [alertView release]; return; } // ここまで SimpleClientAppDelegate *app = (SimpleClientAppDelegate*)[[UIApplication sharedApplication] delegate]; [app.model tweet:tweetTextView.text]; [tweetTextView resignFirstResponder]; }
タイムラインの更新時にアラートパネルが表示される
ツイートとタイムラインの取得は、ともにTwitterEngineを使って実行するので、サーバからのレスポンスはrequestSucceededメソッドが実行されます。
現在はrequestSucceededメソッドが実行されると、ツイートに成功したことを示すアラートを表示するので、タイムラインの取得時も同じアラートが表示されてしまいます。
そこで、タイムラインの取得時はアラートを表示させないように、requestSucceededメソッド内でツイートか、タイムライン取得かを判定します。
TwitterEngineを使ってサーバにデータを送信するときに、connectionIdentifierという識別子(文字列)が返ってきます。
ツイート時はこれを保持して、requestSucceededメソッド内で比較することで、処理を振り分けることができます。
connectionIdentifierを保持する方法として、ModelクラスにtweetIdentifierというNSString型のメンバ変数を定義し、プロパティ宣言でretainオプションを使います。@interface Model : NSObject { XAuthTwitterEngine *twitterEngine; NSArray *dataArray; NSString *tweetIdentifier; // 追加行 } @property (nonatomic,retain) XAuthTwitterEngine *twitterEngine; @property (nonatomic,retain) NSArray *dataArray; @property (nonatomic,retain) NSString *tweetIdentifier; // 追加行 ... 以降省略
ツイート時のみ、TwitterEngineからの返り値をtweetIdentifierで保持し、requestSucceededメソッド内で文字列比較のメソッドであるisEqualToStringを使って比較し、合致していればツイートと判断してアラートを表示させます。
@implementation Model @synthesize twitterEngine, dataArray, tweetIdentifier; // 追加 - (void)buildTwitterEngine { ... 以降省略 - (void)tweet:(NSString*)message { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; self.tweetIdentifier = [twitterEngine sendUpdate:message]; // 追加 } - (void)requestSucceeded:(NSString*)connectionIdentifier { if ( self.tweetIdentifier != nil && [connectionIdentifier isEqualToString:self.tweetIdentifier] ) //追加行 { //追加行 [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Tweet successful" message:@"You got it!" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; [alertView show]; [alertView release]; } //追加行 }
終わりに
以上でTwitterクライアントの制作は終了です。
次のシリーズ開始までしばし皆さまとお別れです。いままでご愛読ありがとうございました。
なお、ここまでのソースコードを含むプロジェクト一式は、ここをクリックするとダウンロードされます。
※Xcode 3.2.6+iOS SDK 4.3を使ってビルドしました。 -
iPhone SDKではじめるサンデープログラミング (33)
この記事は、2011年06月08日に掲載されました。皆さんご存じの通り、WWDCでたくさんの新技術が発表されました。基調講演のライブカバレッジを追いかけてた方の多くは、日中眠気と闘っていたのではないでしょうか(^ ^)。Lion、iOS 5、iCloud…怒濤のごとく発表された内容を見る限り、順調に進化を遂げたOSと新しいサービスによって、今後もますますAppleを取り巻く環境が盛り上がっていくことでしょう。ただし、これらの技術を使う側にあるプログラマは、今まで以上にその習得に忙しくなるでしょう。
発表ではMission Control、Launchpad、Notification Centerなど斬新で見た目の派手な新機能に驚かされました。同時に、地味ではあるけれど、小粒でうれしい機能もいくつかありました。カメラのシャッターがボリュームボタンでも切れるのは、多くの人が待ち望んでいた機能ではないでしょうか。地味といえばTwitterへの対応もiOS 5の重要な機能として紹介されました。TwitterのAPIがiOS SDKに追加されているかどうかはわかりませんが、この連載のようにオープンソースを使わなくても、OSレベルで実装できるようになることを期待しています。
さて今回は、ホームタイムラインの表示に対応させます。前回までに実装したプログラムでは、自分のツイートしかタイムラインに表示されません。フォロアーのツイートも同時に表示したいと考えても、TwitterEngineにはなぜかホームタイムラインのデータを取得するメソッドがありません。そこで、TwitterEngineに新しいメソッドを定義して、それを実現したいと思います。MGTwitterEngine.mにプログラムを追加
ホームタイムラインを取得するためのメソッドを、MGTwitterEngineに追加します。
もともとある「getUserTimelineFor」メソッドの内容を複製(コピペ)して、TwitterサーバにリクエストするAPI名を「home_timeline」に変更し、不要なコードも削除します。- (NSString *)getHomeTimelineFor { NSString *path = [NSString stringWithFormat:@"statuses/home_timeline.%@", API_FORMAT]; MGTwitterRequestType requestType = MGTwitterUserTimelineRequest; return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil requestType:requestType responseType:MGTwitterStatuses]; }
Model.mのプログラムを変更
いま追加したメソッドを、takeTimelimeメソッド内で呼びます。- (void)takeTimeline { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; // [twitterEngine getUserTimelineFor:nil sinceID:0 startingAtPage:0 count:0]; // 削除行 [twitterEngine getHomeTimelineFor]; // 追加行 }
以上でソースコードの追加は終わりです。
ビルドして実行すると、ホームタイムラインが表示されると思います。接続環境のチェック
Twitter Engineを使う前に、インターネットへの接続環境をチェックすることで、未接続の場合にそのことをユーザに促すことができます。Reachabilityのダウンロード
インターネットへ接続できるかどうかの判定は、SystemConfigurationフレームワークを使います。
直にAPIを呼ぶよりも、Appleのサンプルコード(Reachability)を使うと容易に実装できます。
iOS Dev Centerにサンプルコードがありますので、まずはダウンロードします。Reachabilityを組み込む
ダウンロードしたReachabilityフォルダの中の、Reachabilityクラスファイルをプロジェクトフォルダにコピーします。次に、Reachability.hとReachability.mを、プロジェクトウインドウにドラッグ&ドロップします。
フレームワークの追加
プロジェクトウインドウのFrameworksフォルダの上を右クリックし、メニューから「追加」→「既存のフレームワーク…」を選択します。
表示されるフレームワークのリストから「SystemConfiguration.framework」を選択し、「追加」ボタンを押します。Model.hにプログラムを追加
インターネットへの接続状況を確認するための、hasConnectionToInternetメソッドを宣言します。@interface Model : NSObject { XAuthTwitterEngine *twitterEngine; NSArray *dataArray; } @property (nonatomic,retain) XAuthTwitterEngine *twitterEngine; @property (nonatomic,retain) NSArray *dataArray; - (void)buildTwitterEngine; - (void)takeAccessTokenWithUsername:(NSString*)username password:(NSString*)password; - (void)tweet:(NSString*)message; - (void)takeTimeline; - (BOOL)hasConnectionToInternet; // 追加行 @end
Model.mにプログラムを追加
まずReachabilityクラスのヘッダをソースの先頭に読み込みます。#import "Model.h" #import "XAuthTwitterEngine.h" #import "Reachability.h" // 追加行
Reachabilityクラスを使った、インターネットへの接続状況をチェックするメソッドを実装します。
Reachabilityクラスをシングルトンとして使います。インスタンスを生成したらステータスを取得して、その内容をチェックするだけです。
currentReachabilityStatusメソッドの返り値が「ReachableViaWWAN」なら3G回線、「ReachableViaWiFi」ならWi-Fi回線との接続が確立していることを意味します。- (BOOL)hasConnectionToInternet { Reachability *internetReach = [Reachability reachabilityForInternetConnection]; NetworkStatus netStatus = [internetReach currentReachabilityStatus]; BOOL retValue = NO; if ( netStatus == ReachableViaWWAN || netStatus == ReachableViaWiFi ) retValue = YES; return retValue; }
どこでインターネットの接続チェックをするかは、アプリの仕様によって様々です。今回はアプリ起動時にチェックしたいと思います。TwitterEngineを生成するbuildTwitterEngineメソッドに、 hasConnectionToInternetメソッドを実行して、接続していないときにアラートを表示するようにします。
- (void)buildTwitterEngine { // ここから追加 if ( ![self hasConnectionToInternet] ) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Could not connect to internet." delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; [alertView show]; [alertView release]; return; } // ここまで追加 self.twitterEngine = [[XAuthTwitterEngine alloc] initXAuthWithDelegate:self]; self.twitterEngine.consumerKey = kOAuthConsumerKey; self.twitterEngine.consumerSecret = kOAuthConsumerSecret; NSLog( @"auth = %d", [self.twitterEngine isAuthorized] ); }
ビルドして実行する前に、iPhoneの設定でインターネットの接続をオフにしましょう。設定で「機内モード」をオンにするのが最も簡単です。この状態でアプリを起動すると、アラートが表示されるはずです。
今回はここまで。次回はこのシリーズの最終回として、Twitterクライアントアプリとして仕上げます。
なお、ここまでのソースコードを含むプロジェクト一式は、ここをクリックするとダウンロードされます。
※Xcode 3.2.6+iOS SDK 4.3を使ってビルドしました。 -
iPhone SDKではじめるサンデープログラミング (32)
この記事は、2011年05月25日に掲載されました。前回でツイートの実装を終えましたので、今回はタイムラインの表示にとりかかります。
タイムラインもツイート同様、Twitter Engineを使えば簡単に取得することができます。
タイムラインをの表示にはテーブルビュー(UITableView)を使います。
TwitterEngineを使って読み込んだタイムラインのデータは、NSArray(配列)として取得できるので、それをUITableViewのデータソースとして提供すれば実装できます。
具体的には、UITableViewControllerのサブクラスを作ってタブバーに追加し、ユーザはタブバーでタイムラインのビューを表示できるようにします。UITableViewControllerクラスの作成
ライムラインを表示するためのビューコントローラを作成します。
ファイルメニューから「新規ファイル」を選択してテンプレートウインドウを表示させます。
「UIViewController subclass」を選択、「UITableViewController subclass」にチェックを入れて「次へ」ボタンを押します。ファイル名は「TimelineTableViewController.m」で作成します。
UITableViewControllerの追加
Interface BuilderでMainWindow.xibを開き、Libraryパレットから「UITableViewController」を探して、ウインドウ用のTabBarの一番右に追加します。Inspectorパレットで、配置したUITableViewControllerのNIB Nameを「TimelineTableViewController」に変更します。
以上でライムラインを表示するビューコントローラの準備が整いました。
TimelineTableViewController.mにプログラムを追加する
modelオブジェクトにアクセスするために、SimpleClientAppDelegateクラスとModelクラスのヘッダを読み込みます。
TimelineTableViewControllerがロードされるタイミングで実行されるviewDidLoadメソッドには、Modelクラスからタイムラインのデータ取得が完了したときにコールされる”UpdateTimeline”通知(NSNotification)を登録します。#import "TimelineTableViewController.h" #import "SimpleClientAppDelegate.h" // 追加行 #import "Model.h" // 追加行 @implementation TimelineTableViewController ・・・<中略>・・・ - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTimeline) name:@"UpdateTimeline" object:nil]; // 追加行 // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; }
”UpdateTimeline”通知を受けたときに実行されるメソッドです。
UITableViewオブジェクトに対してreloadDataメソッドを実行し、テーブルビューの内容を再読み込みします。- (void)updateTimeline { [self.tableView reloadData]; }
viewWillAppearは、Viewが表示される直前に実行されるデリゲートメソッドです。
このメソッド内に、ModelクラスのtakeTimelineメソッドをコールします。
つまり、タブバーで一番右のボタンが押されてTimelineTableViewControllerに切り替えられたときに、最新のタイムラインをTwitterサーバから読みこみます。- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; SimpleClientAppDelegate *app = (SimpleClientAppDelegate*)[[UIApplication sharedApplication] delegate]; // 追加行 [app.model takeTimeline]; // 追加行 }
セクションの数を返すデータソースメソッドには、今回はセクションがひとつしかないので「1」を返します。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1; // 変更行
}セクション内のデータの数を返すデータソースメソッドには、ModelクラスのdataArrayが持つデータの数を返します。
もし、dataArrayがnilの場合(タイムラインのデータが取得できていない)、0を返します。- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. SimpleClientAppDelegate *app = (SimpleClientAppDelegate*)[[UIApplication sharedApplication] delegate]; // 追加行 if ( app.model.dataArray != nil ) // 追加行 return [app.model.dataArray count]; // 追加行 return 0; }
セルの表示メソッドであるcellForRowAtIndexPathには、セルのスタイルをUITableViewCellStyleSubtitleに変更します。
これで、画像の表示と2行のテキスト表示に対応できます。
ModelクラスのdataArrayから、表示すべき行(indexPath.row)のデータ(NSDictionary)を取得します。
取得したNSDictionaryから、セルに表示するための以下の内容を取得し、UITableViewCellのtextLabel、detailTextLabel、imageViewにそれぞれ設定します。 - ツイート内容 “text” キー
- ユーザ名 “user” キーの中の ”screen_name” キー
- アイコン画像URL “user” キーの中の “profile_image_url” キー
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; // 変更行
}
// Configure the cell...
// ここから追加
SimpleClientAppDelegate *app =
(SimpleClientAppDelegate*)[[UIApplication sharedApplication] delegate];
if ( app.model.dataArray != nil )
{
NSDictionary *dict = [app.model.dataArray objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:@"text"];
NSDictionary *userDict = [dict objectForKey:@"user"];
cell.detailTextLabel.text = [userDict objectForKey:@"screen_name"];
NSURL *url =
[NSURL URLWithString:[userDict objectForKey:@"profile_image_url"]];
NSData *imageData = [NSData dataWithContentsOfURL:url];
cell.imageView.image = [UIImage imageWithData:imageData];
}
// ここまで
return cell;
}
Model.hにプログラムを追加する
タイムラインのデータを保持するために、NSArray型のdataArrayを宣言し、外部のクラスからアクセスできるように@property宣言します。
また、タイムラインを取得するためのtakeTimelineメソッドも宣言します。
@interface Model : NSObject {
XAuthTwitterEngine *twitterEngine;
NSArray *dataArray; // 追加行
}
@property (nonatomic,retain) XAuthTwitterEngine *twitterEngine;
@property (nonatomic,retain) NSArray *dataArray; // 追加行
- (void)buildTwitterEngine;
- (void)takeAccessTokenWithUsername:(NSString*)username password:(NSString*)password;
- (void)tweet:(NSString*)message;
- (void)takeTimeline; // 追加行
@end
Model.mにプログラムを追加する
まず、dataArrayをSynthesize宣言します。
@implementation Model
@synthesize twitterEngine, dataArray;
takeTimelineメソッドには、setNetworkActivityIndicatorVisibleでステータスバーのインジケータのアニメを開始します。
つぎに、TwitterEngineのgetUserTimelineForメソッドをコールして、非同期にタイムラインのデータを取得します。
- (void)takeTimeline {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[twitterEngine getUserTimelineFor:nil sinceID:0 startingAtPage:0 count:0];
}
タイムラインの取得に成功すると、TwitterEngineのデリゲートメソッドであるstatusesReceivedが実行されます。
setNetworkActivityIndicatorVisibleでステータスバーのインジケータのアニメを停止します。
引数のstatusesがタイムラインのデータなので、自身のdataArrayメンバー変数に代入して保持します。
最後に”UpdateTimeline”通知を実行し、テーブルビューの内容を再読み込みさせます。
- (void)statusesReceived:(NSArray*)statuses
forRequest:(NSString*)connectionIdentifier {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.dataArray = statuses;
NSNotification *notification =
[NSNotification notificationWithName:@"UpdateTimeline" object:self userInfo:nil];
[[NSNotificationCenter defaultCenter] postNotification:notification];
}
以上で自分のツイートがタイムラインとしてリスト表示します。
ビルド実行して、実際に表示されるか試してみましょう。
今回はここまで。次回はホームタイムラインの表示に挑戦します。
なお、ここまでのソースコードを含むプロジェクト一式は、ここをクリックするとダウンロードされます。
※Xcode 3.2.6+iOS SDK 4.3を使ってビルドしました。
iPhone SDKではじめるサンデープログラミング (31)
前回の作業でxAuth認証が実装できましたので、これでTwitterサーバへのアクセスができます。
今回はツイート機能を追加します。テキストビューに入力した文字をツイートする単純なプログラムですが、短いコードで実装する過程で、xAuthTwitterEngineの利便性を実感できるでしょう。
UIViewControllerのサブクラスを作成
ツイート用のビューコントローラを作成します。
ファイルメニューから「新規ファイル」を選択してテンプレートウインドウを表示させます。
「UIViewController subclass」を選択、「With XIB for user interface」にチェックを入れて「次へ」ボタンを押します。
ファイル名は「TweetViewController.m」とします。
TweetViewController.hにプログラムを追加
ツイートする文章を入力するための、UITextViewに接続するOutlet変数を宣言します。
また、ツイートボタンを押したときのアクションメソッドも宣言します。
@interface TweetViewController : UIViewController {
IBOutlet UITextView *tweetTextView; // 追加行
}
- (IBAction)tweetButtonPushed; // 追加行
@end
TweetViewController.xibにオブジェクトを配置
プロジェクトウインドウから、TweetViewController.xibをダブルクリックします。
インスペクタパレットで、ViewのBottom表示設定を「Tab Bar」に変更します。
UITextViewとUIButtonをViewの上に配置し、大きさを調整します。
下方からキーボードが覆い被さるので、全体的に上の方に配置した方がよいでしょう。
UITextViewをtweetTextViewにOutlet接続します。
UIButtonをtweetButtonPushedメソッドにAction接続します。
Model.hにプログラムを追加
Xcodeに戻り、Model.hにtweetメソッドを宣言します。
@interface Model : NSObject {
XAuthTwitterEngine *twitterEngine;
}
@property (nonatomic,retain) XAuthTwitterEngine *twitterEngine;
- (void)buildTwitterEngine;
- (void)takeAccessTokenWithUsername:(NSString*)username
password:(NSString*)password;
- (void)tweet:(NSString*)message; // 追加行
@end
Model.mにプログラムを追加
tweetメソッド内には、TwitterEngineのsendUpdateメソッドを使って、引数の文字列をTwitterサーバ送信しています。
送信する前に、UIApplicationのsetNetworkActivityIndicatorVisibleメソッドで、ステータスバーの通信インジケータを有効にします。
- (void)tweet:(NSString*)message {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[twitterEngine sendUpdate:message];
}
ツイートに成功した場合は、TwitterEngineのデリゲートメソッドrequestSucceededが実行されるので、成功したことを伝えるUIAlertViewを表示させています。
また、setNetworkActivityIndicatorVisibleメソッドで通信インジケータを消します。
- (void)requestSucceeded:(NSString*)connectionIdentifier {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Tweet successful"
message:@"You got it!"
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:@"OK", nil];
[alertView show];
[alertView release];
}
ツイートに失敗した場合は、TwitterEngineのデリゲートメソッドrequestFailedが実行されるので、エラー内容をUIAlertViewを表示させています。
また、setNetworkActivityIndicatorVisibleメソッドで通信インジケータを消します。
- (void)requestFailed:(NSString*)connectionIdentifier withError:(NSError*)error {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:@"OK", nil];
[alertView show];
[alertView release];
}
TweetViewController.mにプログラムを追加
ツイートボタンを押したときのプログラムを追加します。
modelオブジェクトにアクセスするために、SimpleClientAppDelegateクラスとModelクラスのヘッダを読み込みます。
tweetButtonPushedメソッドには、modelクラスのtweetメソッドをコールします。引数には、UITextViewに入力されたテキスト(NSString)を渡します。
#import "TweetViewController.h"
#import "SimpleClientAppDelegate.h" // 追加行
#import "Model.h" // 追加行
@implementation TweetViewController
・・・<中略>・・・
// 追加行(ここから)
- (IBAction)tweetButtonPushed {
SimpleClientAppDelegate *app =
(SimpleClientAppDelegate*)[[UIApplication sharedApplication] delegate];
[app.model tweet:tweetTextView.text];
}
// 追加行(ここまで)
MainWindow.xibの設定変更
プロジェクトウインドウから、MainWindow.xibを開きます。
「Second」タブを選択して、インスペクタパレットのNIB Nameを「TweetViewController」に変更します。
インスペクタパレットのClassも同様に「TweetViewController」に変更します。
以上で実装完了です。
アプリをビルドして動かしてみましょう。
エラーが返ってくる場合は、ユーザIDまたはパスワードが間違えている可能性がありますので、もう一度確認してください。
同様に、プログラムも打ち間違えがないか、警告が表示されているか等、全体をチェックしてみましょう。
今回はここまで。次回はタイムライン表示機能に取りかかります。
なお、ここまでのソースコードを含むプロジェクト一式は、ここをクリックするとダウンロードされます。
※Xcode 3.2.6+iOS SDK 4.3を使ってビルドしました。
iPhone SDKではじめるサンデープログラミング (30)
今回からいよいよTwitterクライアントの実装部分に入ります。
まずは、TwitterのユーザID(アカウント)とパスワードを入力させて、xAuth認証の処理を実装します。
具体的には、XAuthTwitterEngineのメソッドを使って、ユーザIDとパスワードをサーバに送信し、アクセストークン文字列を取得します。
前回は、これらの処理が一切実装されていなかったので、TwitterEngineは認証の結果としてNOを返しました。
認証処理は、Twitterの機能にアクセスする上では避けて通れない部分ですので、使い方をマスターしましょう。
FirstViewController.hにプログラムを追加
まずは、ユーザインターフェイスの部分を作っていきます。
認証にはTwitterのユーザID(アカウント)とパスワードが必要ですので、それらを入力するためのユーザインターフェイスを作ります。
ヘッダファイルに、ユーザIDとパスワード入力用のUITextFieldと接続するためのインスタンス変数をOutlet宣言します。
また、認証ボタンを押したときのアクションメソッド(authButtonPushed)も宣言します。
#import
@interface FirstViewController : UIViewController {
IBOutlet UITextField *userField, *passField; // 追加行
}
- (IBAction)authButtonPushed; // 追加行
@end
FirstView.xibにオブジェクトを配置
プロジェクトウインドウから、FirstView.xibをダブルクリックして開きます。Interface Builderに切り替わるので、表示されるビューを示す「View」をダブルクリックします。
Viewの上には、テンプレートで定義された不要なオブジェクトが配置されているのですべて削除します。
全体をマウスで囲むことで選択できますので、deleteキーを押して削除します。
UILabelでタイトルを作り、その下にUITextFieldで入力欄をレイアウトします。
パスワード用のUITextFieldには、Inspectorパレットで「secure」のチェックを入れるとよいでしょう(入力した文字が●で隠されます)。
最後に認証を意味する「Authentication」ボタン(UIButton)を配置します。
File’s OwnerのClassを「FirstViewController」に変更します。
ユーザIDとパスワード入力用のUITextFieldをOutlet接続し、「Authentication」ボタンをAction接続します。
FirstViewController.mにプログラムを追加
認証を表す「Authentication」ボタンを押したときのプログラムを追加します。
modelオブジェクトにアクセスするために、SimpleClientAppDelegateクラスとModelクラスのヘッダを読み込みます。
authButtonPushedメソッドには、modelクラスの「takeAccessTokenWithUsername」メソッドをコールします。
引数には、ユーザIDとパスワード用UITextFieldの内容を渡します。
#import "FirstViewController.h"
#import "SimpleClientAppDelegate.h" // 追加行
#import "Model.h" // 追加行
@implementation FirstViewController
・・・<中略>・・・
// 追加行(ここから)
- (IBAction)authButtonPushed {
SimpleClientAppDelegate *app =
(SimpleClientAppDelegate*)[[UIApplication sharedApplication] delegate];
[app.model takeAccessTokenWithUsername:userField.text password:passField.text];
}
// 追加行(ここまで)
Model.hにプログラムを追加
「takeAccessTokenWithUsername」メソッドを宣言します。
@interface Model : NSObject {
XAuthTwitterEngine *twitterEngine;
}
@property (nonatomic,retain) XAuthTwitterEngine *twitterEngine;
- (void)buildTwitterEngine;
- (void)takeAccessTokenWithUsername:(NSString*)username
password:(NSString*)password; // 追加行
@end
Model.mにプログラムを追加
takeAccessTokenWithUsernameメソッド内には、TwitterEngineのexchangeAccessTokenForUsernameメソッドをコールします。
このメソッドは、ユーザ名とパスワードを引数として渡し、Twitterサーバからアクセストークン文字列を非同期で取得するためにあります。
- (void)takeAccessTokenWithUsername:(NSString*)username
password:(NSString*)password {
[self.twitterEngine exchangeAccessTokenForUsername:username password:password];
}
storeCachedTwitterXAuthAccessTokenStringは、TwitterEngineの持つデリゲートメソッドで、認証が成功してアクセストークン文字列が取得できたときに実行されます。
取得したアクセストークン文字列を、NSUserDefaultsクラスで、Preferencesファイルに書き込みます。
こうすることで、次回からはサーバへはアクセスせずに、Preferencesファイルを読み込むことでアクセストークン文字列を使うことができます。
試験的にNSLogを使って、でアクセストークン文字列をコンソールウインドウに出力しています。
- (void)storeCachedTwitterXAuthAccessTokenString:(NSString*)tokenString
forUsername:(NSString*)username {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:tokenString forKey:@"TokenString"];
[userDefaults synchronize];
NSLog( @"Token String = %@", tokenString );
}
twitterXAuthConnectionDidFailWithErrorも、TwitterEngineの持つデリゲートメソッドで、 認証時にエラーが発生したときに実行されます。
エラー時は、UIAlertViewを使ってアラートパネルに表示させます。
- (void) twitterXAuthConnectionDidFailWithError: (NSError *)error {
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:@"Authentication error"
message:@"Please check your username and password and try again."
delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
[alertView show];
[alertView release];
}
cachedTwitterXAuthAccessTokenStringForUsernameは、TwitterEngineの持つデリゲートメソッドで、 TwitterEngineがアクセストークン文字列を要求したときに実行されます。
ここでは、Preferencesファイルから読み込んだアクセストークン文字列を返しています。
- (NSString*)cachedTwitterXAuthAccessTokenStringForUsername:(NSString*)username {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
return [userDefaults objectForKey:@"TokenString"];
}
ビルドして実行すると、コンソールウインドウにアクセストークン文字列が出力されるはずです。
もし何も出力されなかったり、エラーがコンソールに書き出される場合は、正しく認証できていないことが考えられます。
Consumer KeyやConsumer Secretが正しくソースに転記されているか、ユーザIDとパスワードは正しく入力されているか、などをチェックしてみましょう。
今回はここまで。次回はツイート機能を実装します。
なお、ここまでのソースコードを含むプロジェクト一式は、ここをクリックするとダウンロードされます。
※Xcode 3.2.6+iOS SDK 4.3を使ってビルドしました。
iPhone SDKではじめるサンデープログラミング (29)
引き続き、Twitterクライアントアプリの開発を進めます。
まず、Twitterサーバとの認証を行う処理に着手します。
全体のボリュームが大きいので、今回は前半部分を解説します。
とても地味な機能で面白みに欠けますが、認証はTwitterアプリを組み立てる上とても重要な機能です。
これが実装できないと、Twitter APIを呼ぶことができないので、避けて通れない処理になります。
アプリの概要
最終的にどんなアプリが完成するのか?そのイメージを膨らませながらプログラミングすることは重要です。
今回制作するアプリのゴールは、認証・ツイート・タイムラインの3つの機能を提供するアプリです。
Twitterクライアントとしては最低限の機能ですが、開発の過程でTwitterエンジンの使い方を理解して、将来的に自分で更なる機能アップができるようになることが目標です。
具体的には、ユーザIDとパスワードを入力し、アクセストークン文字列を取得する機能を実装します。
アクセストークン文字列は、NSUserDefaultsクラスを使って、設定ファイル(Preferences)に保存され、再起動時にそれを読み込み、以降は自動的に認証できるようにします(これについては次回)。
Modelクラスの作成
TwitterEngineを使った処理は、これから作成するModelクラスが担当し、各ViewControllerがModelクラスをハブとして連携するようなアプリ構造を目指します。
ファイルメニューから「新規ファイル」を選択してテンプレートウインドウを表示させます。
「Objective-C class」を選択、「Subclass of」が「NSObject」を選択して「次へ」ボタンを押します。
ファイル名は「Model.m」で作成します。
MGTwitterEngine.mの変更
今回はlibXMLライブラリを使いませんので、MGTwitterEngine.mに記述されてる「USE_LIBXML」のフラグを1から0に変更します。
MGTwitterEngine.mは、TwitterEngineフォルダの中に入っています。
#import "MGTwitterEngine.h"
#import "MGTwitterHTTPURLConnection.h"
#import "NSData+MGTwitterBase64.h"
#define USE_LIBXML 0 ←1だったところを0に書き換える
Model.hにプログラムを追加
第27回で解説した手続きによって得られた、Consumer KeyとConsumer SecretをModel.hに定義します。
ここに書いてある文字列は使えませんので、皆さんが自分で取得した文字列を入力してください。
また、ModelクラスでTwitterEngineオブジェクトを保有するために、メンバー変数にXAuthTwitterEngineを宣言し、同時にproperty宣言します。これは、TwitterEngineを生成したあとにretainすることと、外部クラスからTwitterEngineのインスタンスにアクセスするためのものです。
最後に、TwitterEngineクラスのインスタンスを生成するための、buildTwitterEngineメソッドを宣言します。
#import <Foundation/Foundation.h>
#define kOAuthConsumerKey @"hsj84wirkl3319sncmfk" // 追加行
#define kOAuthConsumerSecret @"Hg67mreW5fKl98UdsdJp24DenTdks" // 追加行
@class XAuthTwitterEngine;
@interface Model : NSObject {
XAuthTwitterEngine *twitterEngine; // 追加行
}
@property (nonatomic,retain) XAuthTwitterEngine *twitterEngine; // 追加行
- (void)buildTwitterEngine; // 追加行
@end
SimpleClientAppDelegate.hにプログラムを追加
アプリケーションデリゲートとなるSimpleClientAppDelegateクラスには、modelオブジェクトを保有するために、Modelクラスをメンバー変数宣言します(Interface Builderで作成するので、IBOutletとして宣言)。
また、外部のクラスからアクセスできるようproperty宣言します。
#import <UIKit/UIKit.h>
@class Model;
@interface SimpleClientAppDelegate : NSObject {
UIWindow *window;
UITabBarController *tabBarController;
IBOutlet Model *model; // 追加行
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
@property (nonatomic,readonly) Model *model; // 追加行
@end
Modelオブジェクトの作成
Interface Builderに切り替え、MainWindow.xibにNSObjectを追加します。
配置したNSObjectのクラスをModelに変更します。
これで、Modelクラスのインスタンスが作成されるようになります。
ModelオブジェクトをOutlet接続
作成したModelオブジェクトを、SimpleClientAppDelegateのOutletと接続します。
SimpleClientAppDelegateの中で、Modelクラスに対してアクセスするためです。
Model.mにプログラムを追加
XAuthTwitterEngineを使うために、最初に#import文でヘッダを読み込みます。
property宣言したtwitterEngineを実装させるために、synthesize宣言します。
buildTwitterEngineメソッドの中身は、XAuthTwitterEngineを生成し、Consumer KeyとConsumer Secretを設定しています。
最後に、認証できているかどうかを確認するために、NSLogでコンソールに出力します。
#import "Model.h"
#import "XAuthTwitterEngine.h" // 追加行
@implementation Model
@synthesize twitterEngine; // 追加行
// 追加行(ここから)
- (void)buildTwitterEngine {
self.twitterEngine =
[[XAuthTwitterEngine alloc] initXAuthWithDelegate:self];
self.twitterEngine.consumerKey = kOAuthConsumerKey;
self.twitterEngine.consumerSecret = kOAuthConsumerSecret;
NSLog( @"auth = %d", [self.twitterEngine isAuthorized] );
}
// 追加行(ここまで)
@end
SimpleCrientAppDelegate.mにプログラムを追加
buildTwitterEngineメソッドはModelクラスにあるので、先頭でModelクラスのヘッダを読み込みます。
また、他のクラスからも利用できるようにsynthesize宣言します。
アプリが起動した直後(didFinishLaunchingWithOptions)に、TwitterEngineを生成するためのbuildTwitterEngineメソッドを
Modelクラスのインスタンスに対して実行します。
#import "SimpleClientAppDelegate.h"
#import "Model.h" // 追加行
@implementation SimpleClientAppDelegate
@synthesize window;
@synthesize tabBarController;
@synthesize model; // 追加行
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the tab bar controller's view to the window and display.
[model buildTwitterEngine]; // 追加行
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
実行結果
ちょっと長くなりましたが、ようやくModelクラスの作成、TwitterEngineの生成メソッド、それらを呼ぶプログラムが出来上がりました。
ではビルドメニューから「ビルドと実行」を選択して、iPhoneシミュレータ上で実行してみましょう。
実行したらXcodeに切り替えて、実行メニューから「コンソール」を選択します。
コンソールウインドウには、buildTwitterEngineメソッド(Model.m)の一番最後に書いたNSLogが実行されて、その内容が表示されます。
おそらく「auth = 0」と表示されているはずです。
これは [self.twitterEngine isAuthorized] の返り値がを示すもので、TwitterEngineが認証できていないことを意味します。
つまり、[self.twitterEngine isAuthorized] がYESを返すようにすれば、認証が成功したことになります。
原因は、ユーザIDとパスワードをTwitterEngineに渡していないため、アクセストークンをまだ取得できていないためです。
今回はここまでです。次回はユーザIDとパスワードを設定させる画面とそのソースコードを入力し、認証の処理を完成させます。
iPhone SDKではじめるサンデープログラミング (28)
前回はTwitter APIを使うためのアプリ登録や、Consumer Keyの取得を解説しました。
今回から、実際にXcodeでTwitterクライアントアプリを作っていきます。
ところで、今月の初めにiOSアプリ開発環境である「Xcode」のバージョン4がリリースされました。
いままでのバージョン3は、XcodeとInterface Builderがそれぞれ独立していましたが、両者がひとつのアプリになり、見た目も機能も大幅に変わりました。
しかも、いままではApple ID+Developer登録さえすれば、無料でXcodeを含むiOS SDKをダウンロードできたのですが、Xcode 4からは有料となり、Mac App Storeで600円で販売されています。
年会費を必要とする「iOS Developer Program」または「Mac Developer Program」に入会していればダウンロードできますが、年間10,800円の費用が発生します。今までのように誰でも気軽に入手することが出来なくなったのは残念です。
本来ならば最新の開発ツールであるXcode 4での解説をすべきですが、まだリリース直後なので筆者も十分に使いこなせていないことと、Xcode 3ならば今まで通り無料で入手できることを考慮して、Xcode 3を使ってアプリを組み立てていきます。
なお、Xcode 3の最新バージョンは3.2.6で、最新のiOS SDK 4.3が利用できます。
プロジェクトの作成
Xcodeを起動して、ファイルメニューから「新規プロジェクト」を選択すると、新規プロジェクトのテンプレートを選択するダイアログが表示されます。
左側のリストからiOSの「Application」をクリックし、右側に表示されるアイコン群から「Tab Bar Application」を選択、最後に右下の「選択…」ボタンを押します。
プロジェクトフォルダの保存先を指定するためのシートダイアログが表示されます。今回は名前を「SimpleClient」、保存先を「デスクトップ」と指定し、 保存ボタンを押します。
Twitter Engineのインストール
第26回でダウンロードした「XAuthTwitterEngineDemo」フォルダの中にある「Libraries & Headers」フォルダを、プロジェクトフォルダにコピーします。
続いて、コピーしたフォルダ名を「TwitterEngine」に変更します。
フォルダ名はそのままでもいいのですが、筆者はわかりづらいと思い、TwitterEngineと変更しました。
TwitterEngine内の以下のファイルを削除します。
「LibXML」というワードを含むファイルは、libxml2ライブラリを使うパーサです。
「YAJL」というワードを含むファイルは、JSONライブラリを使うパーサです。
今回は標準のXMLライブラリを使用しますので、混乱を避けるためにこれらの不要なファイルを削除しました。
つぎに「TwitterEngine」フォルダを、プロジェクトウインドウの「Classes」フォルダにドラッグ&ドロップします。
以上でTwitterEngineを使用する準備が整いましたが、今回のXcodeの操作実習はここまでです。
最後にTwitterの認証とAPIの基礎的なことを解説します。
認証とAPI
基本的にAPIを利用と認証はセットです。Twitter社が許可していないアプリから、APIが利用されることを防ぐために、認証プロセスは重要な処理になります。
認証が必要ないAPIもいくつかありますが、大半のAPIは認証を必要としますので、認証なしではツイートやタイムラインの取得など、基本的なTwitterの機能は実装できません。
xAuthの認証プロセス
認証にはアクセストークン文字列(Access Token String)が必要です。
アクセストークン文字列はユーザアカウントごとに存在し、それを得るには、ユーザID、パスワード、Consumer Key文字列、Consumer Secret文字列の4点セットをTwitterサーバに送信します。
Consumer KeyとConsumer Secretの取得方法は第27回を参照してください。
正しく認証が完了すると、サーバからはアクセストークン文字列が返ってくるので、アプリ側はそれを保存します。
以降Twitter APIを使うときには、保存したアクセストークン文字列を読み込むように実装します。
xAuthのポリシー
ユーザIDとパスワードをアプリ側で保存しないことが大前提です。
セキュリティの観点から、不用意に重要な情報(ユーザIDとパスワード)をアプリ側で管理すべきではない、というのが基本姿勢です。
Twitter APIの利用に必要なアクセストークン文字列は、今のところ有効期限が設定されていないので、アプリ内に保存して使うときに読み込む方法が一般的です。
今回はここまで。次回はユーザIDとパスワードを使って認証し、アクセストークンを取得するプログラムを作成します。
iPhone SDKではじめるサンデープログラミング (27)
前回はTwitterエンジンをダウンロードしました。
あとは、iOSアプリのプロジェクトにTwitterエンジンを組み込めば、アプリ開発を始めることができます。
ただし、Twitterアプリを開発するにはもうひとつやることがあります。
それは、Twitter社へのアプリ登録とxAuth認証許可です。
App Storeへの登録時にApple社に対して申請するように、Twitterのクライアントアプリを開発するときは、Twitter社に事前にアプリの内容を登録する必要があります。
また、xAuth認証にはConsumer KeyとConsumer Secretというペアの認証キーを使いますので、それをTwitter社から受け取ることが重要です。
アプリの登録
xAuthまで含めたTwitterサーバへのアクセスを実行するには、大きく分けて2つのステップでTwitter社に申請します。
最初にアプリの登録を行い、その上でxAuthの認証が許可されるように申請します。
おっと、ここで大事なことを。アプリの登録にはTwitterのアカウントが要ります。Twitterのアカウントがないのにアプリを作る人は余りいないと思いますが、念のため。
Twitterにログインしてアプリ登録申請サイトを開きましょう。
各入力欄に適切な内容を埋めていきます。
アプリケーションのウェブサイトURLは、もし自前のサイトを持っていないときや、まだリリース前の開発中アプリの場合は、App Storeへのリンクでも大丈夫です(私はそうしました)。
アプリケーションの種類はiOSアプリなので「クライアントアプリケーション」を選択します。
アクセスタイプは「Read & Write」を選択します。
タイムラインを読むだけのアプリならば「Read-only」でも大丈夫かもしれませんが、ツイート機能を含めたり将来機能を拡張することを考えると「Read & Write」の方が無難でしょう。
アプリケーションのアイコンは無くても大丈夫です。
なお、各項目は登録後も変更できますので、ウェブサイトURLなどはダミーでも大丈夫だと思います。
登録が終わると、Consumer KeyとConsumer Secretの文字列がメールで送られてきます。
この2つはプログラムの中で使用しますので、大事に保存しておきましょう。
xAuth認証許可申請
最後にxAuth認証の使用許可申請メールを送信します。
宛先は「api@twitter.com」です。
英文で申請しなければなりませんが、「こんなアプリを作るので、xAuth認証を使わせてください」という内容と、アプリ名、Twitterアカウント名、アプリ登録で受け取ったConsumer Keyの3点セットを書くだけです。
参考までに私が申請したメールの内容を公開します。
Hello.
I am a developer of "Simple Timeline".
"Simple Timeline" is a iPhone and iPad application, It is a simple twitter client app.
Please apply this app to use xAuth.
Application Name: Simple Timeline
My account: @dreamgardensoft
Consumer Key: a1b2c3d4e5f6g7h8i9
Thank you.
うまくいけば、しばらくしてから「xAuth認証を許可したよ」というメールが届きます。
私の場合、「Simple Timelineとはどんなソフトか?画面ダンプを見せてくれ」との返信があり、何度かやり取りの末やっとxAuth認証が許可されましたので、余裕を持って申請した方が良さそうです。
以上でTwitterアプリを開発する準備が整いました。
次回からXcodeを使って、実際にTwitterアプリを組み立てる作業に移ります。
iPhone SDKではじめるサンデープログラミング (26)
皆さまご無沙汰しておりました。仕事の事情により、連載の再開が遅れてしまったことをお詫び申し上げます。
休載していた間に、新しいMacBook Airが発売になったり、Mac App Storeがスタートしたり、iOSに押され気味のMac OS Xも盛り上がりをみせています。
iPad 2やiPhone 5など、まだ見ぬ未来のハードウェアの噂は絶えないものの、春から夏にかけてはこれらが現実のものとなり、ちょっと気を抜くと世界が一変してしまうエキサイティングな年になりそうです。
新年から心機一転、新しいテーマで連載を始めます。
今回は実用的なアプリ作りに挑戦しようと思い、いろいろ思案した結果、Twitter機能に着目しました。
最近は様々なアプリにTwitterの機能が組み込まれています。
私は受託開発でiPhoneアプリを作ることが日常の業務となっていますが、昨年作ったアプリの約半数にTwitterの機能を組み込みました。
スマートフォンとの相性の良さなのか、世の中の時流なのかわかりませんが、アプリにTwitterの機能を要求する話が絶えません。
そこで、Twitterの基本機能であるツイートとタイムラインの表示を実現するアプリ作りに挑戦します。
Twitter社が運営するコミュニケーションサービスで、全世界で1億人を超えるユーザが登録されており、すでに一般的な用語として社会に浸透してきています。
Twitterの機能はAPIを通じて、自分のアプリに実装することが出来、このことがTwitterの普及をさらに推進していると言えます。
Webアプリやデスクトップアプリ、iPhoneやAndroidなどのスマートフォンのアプリなど、様々なプラットフォームをサポートします。
Twitter APIを使えば、Twitterクライアントはもちろん、botと呼ばれる自動発信プログラムなど、Twitterサーバを利用した幅広いアプリ開発を実現できます。
どのようなAPIが存在するかは、Twitter社が公開する技術資料から知ることが出来ます。
また、Twitter APIに関する書籍も刊行されており、筆者は辻村 浩氏が書かれた「Twitter APIプログラミング」をもとに勉強しました。
Twitter APIを使うには
iOS SDKには、Twitterに関連するフレームワークは一切ありません。
つまり、Twitter APIを利用するコードはすべて自前で開発する必要があります。
以前はBasic認証により、ユーザの認証を行っていたため、容易にツイートしたりタイムラインのデータを取得できました。
しかし、セキュリティの強化という名目で、昨年の8月でBasic認証を廃止され、代わりにOAuth認証が採用されました。
OAuth認証により、なりすましのツイートや不正なアクセスの防止が進みましたが、プログラムの実装は複雑になりました。
オープンソースの活用
自分でTwitterの認証コードやAPIを使うコードを書くことは可能ですが、すでに公開されているTwitter関連のオープンソースを利用することで、労せず質の高いプログラムを作ることができます。
MGTwitterEngine
Matt Legend Gemmell氏が開発した、Objective-CのTwitterライブラリです。
iPhone/iPadに限らず、Mac OS Xのデスクトップアプリにも利用できます。
Matt氏はこのライブラリ以外にも、多くの有用なオープンソースを公開しています。
XAuthTwitterEngine
MGTwitterEngineにxAuthの認証コードを追加したライブラリです。
面倒な認証機能が数行のコードで実装できるので、大変ありがたいです。
MGTwitterEngineを含んだオープンソースなので、開発にはこちらのコード一式を使います。
なお、XAuthTwitterEngineはMITライセンスにおいての使用が認められています。MITライセンスは、数あるライセンスの中で非常に制限の緩いと言われています。
ライセンスの使用規約を守れば、自身のアプリに組み込み、App Storeで公開することが出来ます。
- このソフトウェアを誰でも無償で無制限に扱って良い。但し、著作権表示および本許諾表示を、ソフトウェアのすべての複製または重要な部分に記載しなければならない。
- 作者または著作権者は、ソフトウェアに関してなんら責任を負わない。
いよいよプログラミング、といきたいところですが、Twitter APIを使用するにはTwitter社に開発者登録したり、アプリの登録が必要になります(Appleみたいですね)。
これについては次回に解説いたします。
iPhoneではじめるサンデープログラミング (25)
モグラたたきゲームもいよいよ仕上げです。
連載が丸々一週間遅れてしまったことをお詫び申し上げます。
タイトル画面のローカライズ
プロジェクトウインドウのResourcesグループにある「MoleAttackViewController.xib」を選択して、プロジェクトウインドウの「情報」ボタンを押します。
「一般」タブをクリックして、「ファイルをローカライズ可能にする」ボタンを押します。
もう一度「一般」タブをクリックして、「ローカリゼーションを追加」ボタンを押します。
ポップアップボタンから「Japanese」を選択して追加ボタンを押します。
ローカリゼーションリストに「Japanese」が追加されました。
プロジェクトウインドウの「MoleAttackViewController.xib」を見ると、左に三角形が付いていることがわかります。
三角形をクリックするとサブアイテムが表示され、それぞれ「English」と「Japanese」になっています。
Japaneseをダブルクリックすると、日本語用のMoleAttackViewController.xibがInterface Builderで開きます。
Title Viewを開いてローカライズしてみましょう。
iPhone Simulatorの【設定】→【一般】→【言語環境】→【言語】で言語を日本語や英語に切り替えて、正しく表示されるかチェックしましょう。
アラートのローカライズ
まずは、文字列を定義するファイル「Localizable.strings」を作成します。
プロジェクトウインドウのResourcesグループの上を右クリックして、コンテキストメニューから「追加」→「新規ファイル…」を選択します。
新規ファイルの作成ダイアログが表示されるので、カテゴリリストから「Resources」を選択し、「Strings File」を選択して「次へ」ボタンを押します。
ファイル名のフィールドに「Localizable.string」と入力して、「完了」ボタンを押します。
Resourcesグループに「Localizable.strings」が追加されます。
「Localizable.strings」を選択して、プロジェクトウインドウの「情報」ボタンを押します。
「一般」タブをクリックして、「ファイルをローカライズ可能にする」ボタンを押します。
もう一度「一般」タブをクリックして、「ローカリゼーションを追加」ボタンを押します。
ポップアップボタンから「Japanese」を選択して追加ボタンを押します。
ローカリゼーションリストに「Japanese」が追加されました。
今度は、追加したStringsファイルを各言語ごとに編集します。
プロジェクトウインドウの「Localizable.strings」を見ると、左に三角形が付いていることがわかります。
三角形をクリックするとサブアイテムが表示され、それぞれ「English」と「Japanese」になっています。
それぞれのファイルを開いて、文字列のソースコードを入力します。
最後に「MoleAttackViewController.m」を一部修正して、Localizable.stringsに対応させます。
updateTimeメソッドのアラートパネルを表示する箇所を、Localizable.stringsファイルを読み込むように変更します。
- (void)updateTime {
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent() - startTime;
timeLabel.text = [NSString stringWithFormat:@"%0.1lf", 20 - time];
if ( time >= 20 )
{
timeLabel.text = @"0.0";
[moleTimer invalidate];
[timeTimer invalidate];
NSString *titleStr = NSLocalizedString( @"AlertTitle", nil ); // 追加行
NSString *titleSubStr = NSLocalizedString( @"AlertSubTitle", nil ); // 追加行
UIAlertView *alertView = [[UIAlertView alloc]
/* initWithTitle:@"Game Over" message:@"Thank you for playing." 削除行 */
initWithTitle:titleStr message:titleSubStr // 追加行
delegate:self cancelButtonTitle:nil
otherButtonTitles:@"OK", nil];
[alertView show];
[alertView release];
}
}
以上で、アラートパネルの表示内容が言語環境によって、英語または日本語で表示されます。
まとめ
13回に渡りモグラたたきゲームを作りました。
UIViewControllerで複数のビューを切り替えたり、UITableViewで得点の順位を表示させるなど、iPhoneアプリの基本的な機能を、ゲーム制作を通じて学びました。
また、NSUserDefaultでアプリを終了しても、データが保持するためのコードも学習しました。
この連載で身に付けたことは、ゲーム以外にも応用できると思います。
皆さまの手で新たなiPhoneアプリを開発いただき、サンデープログラミングをお楽しみいただければ幸いです。
今回でモグラたたきの制作は終了です。ご愛読いただき、ありがとうございました。
次回からは別のテーマでサンデープログラミングを楽しみたいと思います。お楽しみに!
iPhoneではじめるサンデープログラミング (24)
暑いが続きますが、皆さまはいかがお過ごしでしょうか?
海やプールが恋しい季節ですが、私はそれとは無縁にクーラーの効いた部屋でもくもくとコーディングに励んでいる毎日です。
こう暑い日が続くと、秋のそよ風が本当に待ち遠しくなりますね。
前回までは、順位を表示するためのビューを切り替えるところまで実装しました。
順位表のビューには、テーブルビューを配置したのですが、内容を表示するプログラムまで実装していなかったので、今回はその部分を追加します。
RankingViewController.hにプログラムを追加する
まず、テーブルビューに不可欠なメソッドを実装するために、UITableViewDataSourceをクラスに宣言します。
また、テーブルにデータを表示するための配列(NSMutableArray)も宣言します。
readScoresとwriteScoresは、アプリが終了してもデータを保持するためにデータを読み書きするメソッドです。
@interface RankingViewController : UIViewController <UITableViewDataSource> /*追加*/ {
IBOutlet UITableView *rankingTableView;
id app;
NSMutableArray *rankingData; // 追加
}
(IBAction)doneButtonPushed:(id)sender;
- (void)readScores; // 追加
- (void)writeScores; // 追加
- (BOOL)insertScore:(int)score; // 追加
@end
RankingViewController.mにプログラムを追加する
viewDidLoadにテーブルビューのData Sourceを設定します。
viewDidLoadメソッド自体がコメントアウトされてますので、まずはコメントを外してください。
- (void)viewDidLoad {
[super viewDidLoad];
rankingTableView.dataSource = self; // 追加
}
NSUserDefaultsは、アプリのデータを保持するときに便利なクラスです。
Property List形式で文字列や数値を保存することができます。
ファイルはサンドボックス内(Library/Preferences)にcom.yourcompany.MoleAttack保存されます。
通常、com.yourcompanyの部分は、自分のドメインなどに書き換えるのですが、その方法は一番最後で解説します。
- (void)readScores {
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSArray *array = [userDefault arrayForKey:@"Scores"];
if ( array == nil )
{
rankingData = [[NSMutableArray alloc] init];
int i;
for ( i = 0; i<10; i++ )
{
[rankingData addObject:[NSNumber numberWithInt:10-i]];
}
}
else
{
rankingData = [[NSMutableArray arrayWithArray:array] retain];
}
}
readScoresとは逆に、順位データを書き込みます。
- (void)writeScores {
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault setObject:rankingData forKey:@"Scores"];
[userDefault synchronize];
}
引数scoreの値を順位データに挿入しYESを返します。もし、10位以内に入っていない場合は、値は挿入されずにNOを返します。
- (BOOL)insertScore:(int)score {
for ( NSNumber *number in rankingData )
{
if ( score>number.intValue )
{
NSNumber *newScore = [NSNumber numberWithInt:score];
NSUInteger index = [rankingData indexOfObject:number];
[rankingData insertObject:newScore atIndex:index];
[rankingData removeLastObject];
[rankingTableView reloadData];
return YES;
}
}
return NO;
}
これはUITableViewDataSourceで定義されているメソッドで、テーブルのセクション数を返してあげます。セクションがないテーブルの場合は常に1です。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
これもUITableViewDataSourceで定義されているメソッドで、セクション内のセルの数を返してあげます。今回はセクションがないので、常にデータの数を返します。
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [rankingData count];
}
これもUITableViewDataSourceで定義されているメソッドで、セルに対して表示すべき内容を設定してあげます。
ここでは、cell.textLabelに順位の得点データを文字列に変換したものを与えています。
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if ( cell == nil )
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier] autorelease];
NSNumber *number = [rankingData objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"#%d --- %d",
indexPath.row+1, number.intValue];
return cell;
}
MoleAttackAppDelegate.mにプログラムを追加する
applicationDidFinishLaunchingメソッドの最後に、readScoresを呼び出すコードを追加します。
これにより、アプリが起動した直後にUser Defaultsから順位のデータが読み込まれます。
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
[window addSubview:viewController.view];
[window makeKeyAndVisible];
[rankingController readScores]; // 追加
}
MoleAttackAppDelegate.hにプログラムを追加する
afterGameOverメソッドを宣言します。
ゲーム終了時にMoleAttackViewControllerから呼ばれます。
@interface MoleAttackAppDelegate : NSObject {
UIWindow *window;
MoleAttackViewController *viewController;
IBOutlet RankingViewController *rankingController;
}
- (void)showRankingTable;
- (void)hideRankingTable;
- (void)afterGameOver:(int)score; // 追加
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet MoleAttackViewController *viewController;
@end
MoleAttackAppDelegate.mにプログラムを追加する
afterGameOverメソッドを実装します。
rankingControllerのinsertScoreに引数scoreを与えます。
戻り値がYESだった場合は、10位以内に入ったことを意味しますので、showRankingTableメソッドを呼んで順位表を表示させます。
最後にwriteScoresで順位のデータをUser Defaultsに保存します。
- (void)afterGameOver:(int)score {
if ( [rankingController insertScore:score] )
{
[self showRankingTable];
[rankingController writeScores];
}
}
MoleAttackViewController.mにプログラムを追加する
ゲーム終了時に、MoleAttackAppDelegateに対して、afterGameOverメソッドを呼びます。引数に現在の得点を渡すため、scoreLabelのtextプロパティから得点の整数を得ています。
- (void)alertView:(UIAlertView *)alertView
didDismissWithButtonIndex:(NSInteger)buttonIndex {
[app afterGameOver:scoreLabel.text.intValue]; // 追加
[self.view addSubview:titleView];
}
以上で、テーブルビューの実装は終わりです。
ビルドして実行したあと、RANKINGボタンを押すと、順位表のテーブルビューが表示されるはずです。
実際にゲームで遊んで、10点以上取るとランクインされて、順位表の内容も変わることを確認してください。
たかが順位を表示するだけで、これだけのプログラムを書かなければ実現できませんが、それだけテーブルビューは柔軟な構造を持っていると言うことです。
各セルの中に画像やコントロールを入れることもできますし、テーブルビューは数多くの機能を提供しています。
長い連載になりましたが、モグラたたきゲームの制作はここで一段落です。
ここまでのプロジェクトはここをクリックするとダウンロードできます。
次回は、アプリアイコンの設定や全体の仕上げをして、このシリーズを終えたいと思います。
付録:User Defaultsファイル名の設定方法
プロジェクトメニューから「アクティブターゲット”MoleAttack”を編集」を選択します。
情報ウインドウが表示されるので、プロパティタブをクリックして、識別子のドメイン名を変更します。デフォルトは「com.yourcompany」になっているので、自身のドメイン名を逆から入力します。
私のドメインは「dreamg.com」なので「com.dreamg」になります。ドメインをお持ちでない方は、メールアドレスでも構いません。
以上の設定で、User Defaultsファイルの名称が変更されます。
iPhoneではじめるサンデープログラミング (23)
今回は順位表示機能の後半です。
RANKINGボタンの配置
MoleAttackViewController.xibのTitle Viewを開いて、STARTボタンの下にUIButtonを配置し、タイトルを「RANKING」に変更します。
RANKINGボタンをrankingButtonPushed:のActionに接続します。
MoleAttackViewController.mにプログラムを追加する
MoleAttackAppDelegateのヘッダを読み込み、RANKINGボタンを押したときのActionメソッドを実装します。
#import "MoleAttackAppDelegate.h" // 追加
...省略...
// 追加メソッド
- (IBAction)rankingButtonPushed:(id)sender {
[app showRankingTable];
}
RankingViewController.hにプログラムを追加する
UITableViewの変数をOutletを宣言します。
また、MoleAttackAppDelegateのメソッドを呼ぶために、id型で変数appを宣言します。
最後に、Doneボタンが押されたときのActionメソッドを宣言します。
@interface RankingViewController : UIViewController {
IBOutlet UITableView *rankingTableView; // 追加
id app; // 追加
}
- (IBAction)doneButtonPushed:(id)sender; // 追加
@end
UITableViewの配置
RankingViewController.xibを開いて、ビューの上にUITableViewを配置します。
下側に余白が出来るようにサイズ調整します。
下側の余白にUIButtonを配置し、タイトルを「Done」に変更します。
UITableViewをrankingTableViewのOutletに接続します。
DoneボタンをdoneButtonPushed:のActionに接続します。
RankingViewController.mにプログラムを追加する
MoleAttackAppDelegateのヘッダを読み込み、 Doneボタンが押されたときの処理を追加します。
#import "MoleAttackAppDelegate.h" // 追加
...省略...
// 追加メソッド
- (IBAction)doneButtonPushed:(id)sender {
[app hideRankingTable];
}
MainWindow.xibでOutlet接続
プロジェクトウインドウのResourcesグループのリストから、MainWindow.xibを開きます。
RankingViewControllerのappに、MoleAttack App DelegateをOutlet接続します。
同様に、MoveAttackViewControllerのappに、MoleAttack App DelegateをOutlet接続します。
MoleAttackAppDelegate.mにプログラムを追加する
順位表示用のビューに切り替えるメソッドを実行します。
まず、先頭にRankingViewControllerのヘッダを読み込むための文を追加します。
#import "RankingViewController.h"
つぎに、showRankingTableとhideRankingTableを実装します。
UIViewのアニメーション機能を使って、メインビューとランキングビューをフリップアニメーションでトランジションします。
- (void)showRankingTable {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.75];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:window cache:YES];
[viewController.view removeFromSuperview];
[window addSubview:rankingController.view];
[UIView commitAnimations];
}
- (void)hideRankingTable {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.75];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:window cache:YES];
[rankingController.view removeFromSuperview];
[window addSubview:viewController.view];
[UIView commitAnimations];
}
ビルドと実行
タイトルビューでRANKINGボタンを押すと、ランキングビューにフリップアニメーションとともに切り替わります。
ランキングビューのDoneボタンを押すと、タイトルビューに切り替わります。
今回はここまで。まだテーブルビューの中身は何もありませんが、次回のソースコードで実装します。
ここまでのプロジェクトはここをクリックするとダウンロードできます。
iPhoneではじめるサンデープログラミング (22)
今回は新たにビューコントローラーとテーブルビューを追加し、得点の順位を表示する機能を実現します。
UIの追加やソースコードの分量も多いので、前半と後半に分けています(今回は前半)。
RankingViewControllerクラスファイルの作成
「ファイルメニュー」から「新規ファイル…」を選択すると、ファイルを作成するためのテンプレートダイアログが表示されます。
「UIViewController subclass」を選択して、「With XIB for User interface」にだけチェックを入れてください。これはソースコードと一緒にxibファイルも作るという意味です(その他はチェックを外します)。
最後に「次へ」ボタンを押します。
ファイル名に「RankingViewController.m」と入力して、「完了」ボタンを押します。
「同時に”RankingViewController.h”も作成」にチェックが入っていることを確認してください。
プロジェクトウインドウのリストに、RankingViewControllerのソースファイル、ヘッダファイル、xibファイルが追加されます。
RankingViewController.xibをResourcesグループに移動します。
MoleAttackAppDelegate.hにプログラムを追加する
RankingViewControllerのOutletを宣言し、順表示のビューを表示させたり、隠したりするメソッドを宣言します。
#import
@class MoleAttackViewController;
@class RankingViewController; // 追加行
@interface MoleAttackAppDelegate : NSObject {
UIWindow *window;
MoleAttackViewController *viewController;
IBOutlet RankingViewController *rankingController; // 追加行
}
- (void)showRankingTable; // 追加行
- (void)hideRankingTable; // 追加行
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet MoleAttackViewController *viewController;
@end
MainWindow.xibにRankingViewControllerを追加
プロジェクトウインドウのResourcesグループのリストから、MainWindow.xibを開きます。
Interface Builderに切り替わり、MainWindow.xibの内容が表示されます。
InspectorパレットからUIViewControllerを見つけて、MainWindow.xibウインドウにドラッグ&ドロップします。
追加したView ControllerのClassを変更するため、Inspectorパレットの一番右のタブを押して、Classのポップアップボタンから「RankingViewController」を選択します。
Inspectorパレットの一番左のタブを押して、NIB Nameのポップアップボタンから「RankingViewController」を選択します。
MoleAttack App DelegateのrankingControllerに、RankingViewControllerをOutlet接続します。
MoleAttackViewController.hにプログラムを追加する
MoleAttackAppDelegateのメソッドを呼ぶために、id型で変数appを宣言します。
また、RANKINGボタンを押したときのActionメソッドを宣言します。
@interface MoleAttackViewController : UIViewController {
IBOutlet UIView *gameView, *titleView;
IBOutlet UILabel *scoreLabel, *timeLabel;
NSTimer *moleTimer, *timeTimer;
CFAbsoluteTime startTime;
id app; // 追加
}
- (void)addScore;
- (IBAction)startButtonPushed:(id)sender;
- (IBAction)rankingButtonPushed:(id)sender; // 追加
@end
まだ途中ですが、今回はここまで。
次回は後半部分を実装し、順位表示機能を完成させます。
iPhoneではじめるサンデープログラミング (21)
ここまでの制作でだいぶゲームとして出来上がってきました。得点表示や制限時間など、ゲームの要素もある程度盛り込まれました。
現在はアプリを起動したらすぐにゲームが始まってしまうので、この部分をもう少し改良してみましょう。
この2点をこれから実装していきます。
MoleAttackViewController.hにプログラムを追加する
タイトル画面となるUIViewの変数(titleView)をOutlet宣言します。
また、スタートボタンを押したときのActionメソッドも宣言します。
@interface MoleAttackViewController : UIViewController {
IBOutlet UIView *gameView;
IBOutlet UIView *titleView; // 追加行
IBOutlet UILabel *scoreLabel, *timeLabel;
NSTimer *moleTimer;
CFAbsoluteTime startTime;
}
(void)addScore;
- (IBAction)startButtonPushed:(id)sender; // 追加行
@end
タイトルビューを追加
MoleAttackViewController.xibを開いて、Interface Builder上でUIViewを追加します。
Viewのタイトル名を「Title View」に変更します。
特にタイトルを変更する必要はありませんが、新規に複数のViewを追加していくと、すべてがViewという名のタイトルになるので、ここに変更する習慣を付けるとオブジェクトの管理が容易になるでしょう。
追加したTitle Viewをダブルクリックで開いて、ビューの上にUILabelとUIButtonを配置します。
UILabelは「Mole Attack」と変更し、フォントの大きさと色を変更します。
UIButtonのタイトルは「START」に変更します。
背景の色は緑色にして半透明にします。
まあ、ここらへんは皆さんの趣味でいろいろ設定してみてください。
つぎに、Title ViewをtitleViewにOutlet接続します。
UIButtonは、startButtonPushed:のActionメソッドに接続します。
MoleAttackViewController.mにプログラムを追加する
viewDidLoadメソッドの中に、titleViewを自身のビューに追加するコードを書きます。
また、 いままでviewDidLoadにあった、ラベルの初期設定やタイマーの生成コードを、startButtonPushedメソッドの中に移動します。
STARTボタンが押されたら、ゲームを始める準備を整え、タイトル画面を消すためにtitleViewを自身のビューから取り除くコード([titleView removeFromSuperview];)を書きます。
(void)viewDidLoad {
RANDOM_SEED();
[super viewDidLoad];
for ( MoleView *view in gameView.subviews )
{
[view setup];
}
[self.view addSubview:titleView]; // 追加行
// 以下のブロックは必要なくなったので削除
/*moleTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self
selector:@selector(showMole) userInfo:nil repeats:YES];
scoreLabel.text = @"0";
startTime = CFAbsoluteTimeGetCurrent();
timeLabel.text = @"20.0";*/
}
// 以下、追加メソッド
- (IBAction)startButtonPushed:(id)sender {
moleTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self
selector:@selector(showMole) userInfo:nil repeats:YES];
scoreLabel.text = @"0";
startTime = CFAbsoluteTimeGetCurrent();
timeLabel.text = @"20.0";
[titleView removeFromSuperview];
}
// ここまで
ゲームオーバー後の処理
ゲームが終了した後、もう一度タイトルに戻る処理を加えます。
MoleAttackViewController.mにプログラムを追加します。
ゲーム終了を表示するアラート画面が閉じた時に実行されるデリゲートメソッド(didDismissWithButtonIndex)を追加し、その中に自身のビューにタイトルビューを追加するコードを書きます。
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[self.view addSubview:titleView];
}
ビルドして実行すると、まずタイトル画面が表示されます。
STARTボタンを押すとゲームが開始し、終了すると「Game Over」と書かれたアラート画面が表示されます。
OKボタンを押すと、タイトル画面に戻ります。
今回はここまで。次回からはテーブルビューを作成し、順位表を画面に表示することに挑戦します。
なお、ここまでのソースコード一式は、ここをクリックするとダウンロードできます(iPhone SDK 4.0+Xcode 3.2.3の環境です)。
iPhoneではじめるサンデープログラミング (20)
前回までのところで、モグラをタップしたら得点が上がり、ゲームとしては大分仕上がりました。
しかし、これではいつまでもモグラを叩き続けることができますので、今回は制限時間の機能を追加してみましょう。
今週が更新がまるまる一週間遅れてしまいました。申し訳ありませんでした。
MoleAttackViewController.hにプログラムを追加
残り時間を表示するためのUILabelの変数(timeLabel)、開始時間を保持する変数(startTime)をそれぞれ宣言します。
@interface MoleAttackViewController : UIViewController {
IBOutlet UIView *gameView;
IBOutlet UILabel *scoreLabel;
IBOutlet UILabel *timeLabel;// 追加行
NSTimer *moleTimer;
CFAbsoluteTime startTime; // 追加行
}
- (void)addScore;
@end
時間ラベルを配置する
MoleAttackViewController.xibを開いて、Interface Builder上でViewにUILabelを追加します。
配置したら、timeLabelにOutlet接続します。
MoleAttackViewController.mにプログラムを追加
開始時間を保持するために、CFAbsoluteTimeGetCurrent()で得た時刻をstartTimeに代入し、時間ラベルに20をセットします。
CFAbsoluteTimeGetCurrent()は、2001年1月1日からの経過時間を秒単位(CFAbsoluteTime型)で取得できるAPIです。保持した時刻と現在の時刻の差により、経過時間を算出しています。
- (void)viewDidLoad {
RANDOM_SEED();
[super viewDidLoad];
for ( MoleView *view in gameView.subviews )
{
[view setup];
}
moleTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self
selector:@selector(showMole) userInfo:nil repeats:YES];
scoreLabel.text = @"0";
startTime = CFAbsoluteTimeGetCurrent(); // 追加行
timeLabel.text = @"20.0"; // 追加行
}
showMoleメソッドの中に、残り時間の表示を更新するプログラムを追加します。
最初に、現在の時刻からゲーム開始時に保持した時刻を差し引き、経過時間を算出して変数timeに代入します。
20からtimeの値を差し引き、残り時間を時間ラベルにセットします。
もし、経過時間(time変数)が20以上だったらゲームを終了させるための処理に入ります。
ここでは、UIAlertViewを生成して、画面にゲーム終了を告げるアラートパネルを表示させています。
- (void)showMole {
NSUInteger index = RANDOM_FLOAT() * [gameView.subviews count];
MoleView *view = [gameView.subviews objectAtIndex:index];
if ( [view.moleImageView isAnimating] == NO )
[view.moleImageView startAnimating];
// ここから追加行
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent() - startTime;
timeLabel.text = [NSString stringWithFormat:@"%0.1lf", 20 - time];
if ( time >= 20 )
{
timeLabel.text = @"0.0";
[moleTimer invalidate];
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:@"Game Over"
message:@"Thank you for playing."
delegate:self
cancelButtonTitle:nil
otherButtonTitles:@"OK", nil];
[alertView show];
[alertView release];
}
// ここまで
}
ビルドと実行
ビルドして実行すると、右上のタイマーラベルの数字が20からカウントダウンされて、0になると「Game Over」のアラートビューが表示されます。
今回の制限時間機能により、20秒間にどれくらいモグラを叩けるかを競い合うことができるようになりました。
次回は、タイトル画面のビューを作り、ゲームオーバー後も再ゲームできるよう、ゲーム開始から終了までをループできるようにします。
iPhoneではじめるサンデープログラミング (19)
今回はモグラをうまく叩いたら(タッチしたら)、得点が上がるようにします。
得点の表示ラベルを追加して、MoleViewにタッチイベントを判定するコードを追加します。
その前に、前回(第18回)のソースコードで誤りがありましたので、この場をお借りしてお詫びいたします。
中盤あたりでMoleAttackViewController.mにソースを追加するところがあるのですが、うまくソースが表示されていませんでした。すでにバックナンバーは修正済みですが、以下のように修正しましたのでお知らせいたします。
ソースコードの先頭に、乱数を容易に扱うためのマクロを記述します。
RANDOM_SEED()は、毎回違った乱数を作りだすためのマクロで、現在の時間を与えることで乱数の種を得ています。
RANDOM_FLOAT()は、0.0〜1.0の実数値で乱数を返すマクロです。
また、第18回までのプロジェクトは、ここをクリックするとダウンロードできます。
MoleAttackViewController.hにプログラムを追加
得点を表示するUILabel(scoreLabel)をOutlet宣言します。
また、得点を上げるメソッド(addScore)も宣言します。
@interface MoleAttackViewController : UIViewController {
IBOutlet UIView *gameView;
IBOutlet UILabel *scoreLabel; // 追加行
NSTimer *moleTimer;
}
- (void)addScore; // 追加行
@end
得点ラベルを配置する
MoleAttackViewController.xibを開いて、Interface Builder上でViewの上にUILabelを追加します。
配置したUILabelとscoreLabelをOutlet接続します。
MoleView.hにプログラムを追加
MoleViewがMoleAttackViewControllerオブジェクトに対してメッセージを送れるように、クラス内にid型でメンバー変数(viewController)を宣言します。
@interface MoleView : UIView {
UIImageView *moleImageView;
id viewController; // 追加行
}
- (void)setup;
@property (readonly) UIImageView *moleImageView;
@end
すべてのMoleViewをOutlet接続する
MoleViewとviewControllerをOutlet接続します。
これはすべてのMoleViewに対して行います(全部で9回)。
MoleAttackViewController.mにプログラムを追加
viewDidLoadの最後に、得点ラベルの値を0にセットします。
ゲームスタート時は得点を0にするためです。
- (void)viewDidLoad {
RANDOM_SEED();
[super viewDidLoad];
for ( MoleView *view in gameView.subviews )
{
[view setup];
}
moleTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self
selector:@selector(showMole) userInfo:nil repeats:YES];
scoreLabel.text = @"0"; // 追加行
}
addScoreは、得点に1加算するメソッドです。
scoreLabelから現在の得点を整数で取得し、1加算した値でNSNumberを作成し、もう一度scoreLabelにセットします。
- (void)addScore {
int score = scoreLabel.text.intValue;
NSNumber *number = [NSNumber numberWithInt:score+1];
scoreLabel.text = number.stringValue;
}
MoleView.mにプログラムを追加
タッチイベントの処理を追加します。
先頭でMoleAttackViewControllerのヘッダをインポートしていますが、これはタッチしたときにaddScoreメソッドを呼ぶためのものです。
touchesBeganは、ビューがタッチされたときに呼ばれるメソッドです。
moleImageViewのisAnimationプロパティを判定し、YESだったらアニメーション中(=モグラが出ている)ということで、得点を1加算してモグラのアニメーションを停止させます。
#import "MoleView.h"
#import "MoleAttackViewController.h"
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ( moleImageView.isAnimating )
{
[viewController addScore];
[moleImageView stopAnimating];
}
}
ビルドと実行
ビルドして実行すると、ピョコピョコ現れるモグラをタッチするごとに、得点ラベルの点数が上がっていきます。
今回はここまで。このままではいつまでも点数が稼げてしまうので、次回は制限時間を設けて、よりゲームらしくアプリを仕上げていきます。
iPhoneではじめるサンデープログラミング (18)
前回は、モグラをアニメーションさせるためのプログラムを追加しました。
モグラの画像をUIViewのアニメーション機能を使って、パラパラマンガのように画像を切り替えて、モグラの動きを表現しました。
今回は、モグラの数を9個(3×3)に増やし、ランダム出たり入ったりするプログラムを追加します。
MoleViewの複製
Interface Builderにて「MoleAttackViewController.xib」を表示し、ウインドウの中に配置したMoleViewを複製します。
オブジェクトの複製は、FileメニューからDuplicateを選択するか、optionキーを押しながら対象のオブジェクトをマウスドラッグすると実行できます。
Xcodeに戻って、ビルドして実行すると、9個のモグラが同時に出たり入ったりするアニメーションが実行されます。
Interface BuilderでMoleViewを複製しただけで、複製したモグラも同じ動作で実行されます。ここら辺がオブジェクト指向プログラミングの良さであります。
ランダムにモグラを登場させる
ここまではモグラが一斉に現れては消える…というアニメーションです。
これではゲームになりませんので、ランダムで交互にモグラが出入りするようにプログラムを追加します。
ここでは、NSTimerというオブジェクトを使って、定期的にメソッドを実行するコードを学習します。
NSTimerとは、指定したメソッドを定期的に実行する便利なクラスです。ユーザからの入力を待つようなプログラムではなく、プログラム自身が能動的に動作するような処理に役立ちます。
MoleAttackViewController.hに、NSTimerのオブジェクトを変数宣言します。
@interface MoleAttackViewController : UIViewController {
IBOutlet UIView *gameView;
NSTimer *moleTimer; //追加行
}
MoleAttackViewController.mにプログラムを追加
ソースコードの先頭に、乱数を容易に扱うためのマクロを記述します。
RANDOM_SEED()は、毎回違った乱数を作りだすためのマクロで、現在の時間を与えることで乱数の種を得ています。
RANDOM_FLOAT()は、0.0〜1.0の実数値で乱数を返すマクロです。
最初にRANDOM_SEED()で乱数を初期化します。
つぎに、viewDidLoad内でNSTimerのオブジェクトを生成します。ここでは、showMoleというメソッドを0.5秒間隔で実行するタイマーを生成しています。
- (void)viewDidLoad {
RANDOM_SEED(); //追加行
[super viewDidLoad];
for ( MoleView *view in gameView.subviews )
{
[view setup];
}
moleTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self
selector:@selector(showMole) userInfo:nil repeats:YES]; //追加行
}
showMoleメソッドは、すべてのMoleViewオブジェクトの中からランダムにひとつを選び出し、そのオブジェクトのアニメーションが止まっていれば、startAnimatingでアニメーションを開始させます。
- (void)showMole {
// 0〜8の乱数をindexに代入
NSUInteger index = RANDOM_FLOAT() * [gameView.subviews count];
// 任意のMoleViewオブジェクトを取得
MoleView *view = [gameView.subviews objectAtIndex:index];
if ( [view.moleImageView isAnimating] == NO ) // アニメーションが止まっていたら
[view.moleImageView startAnimating]; // アニメーション開始
}
MoleView.hにプログラムを追加
moleImageViewを外部のクラスから参照できるように、@property宣言します。
@interface MoleView : UIView {
UIImageView *moleImageView;
}
- (void)setup;
@property (readonly) UIImageView *moleImageView; // 追加行
@end
MoleView.mにプログラムを追加
@property宣言した変数は、ソースファイルでsynthesize宣言します。
@synthesize moleImageView;
setupメソッドの中で、moleImageViewのanimationRepeatCountプロパティの値に1をセットします。アニメーションが1回で終わるようにするためです。
また、startAnimationgはMoleAttackViewControllerで呼んでいるので、ここのコードは削除します。
- (void)setup {
UIImage *image = [UIImage imageNamed:@"hole.png"];
UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
...省略...
moleImageView.animationDuration = 1.0;
// [moleImageView startAnimating]; // この行を削除
moleImageView.animationRepeatCount = 1; // 追加行
}
ビルドと実行
ビルドして実行すると、モグラたたきゲームのように、いろんなところからピョコピョコ出たり入ったりするアニメーションが表示されます。
今回はここまで。次回はモグラをタッチしたら得点が上がるようなプログラムを追加し、よりゲームらしくアプリを仕上げていきます。
iPhoneではじめるサンデープログラミング (17)
今回はモグラの画像をアニメーション表示するプログラムを追加します。
UIImageViewは画像を表示する以外に、複数の画像をパラパラマンガのようにアニメーションさせる機能を持っています。
その機能を使って、モグラが出たり引っ込んだりするアニメーションを実装します。
MoleView.hにプログラムを追加
まず、アニメーション表示用のUIImageViewをクラス定義のメンバー変数に宣言します。
@interface MoleView : UIView {
UIImageView *moleImageView; // 追加行
}
- (void)setup;
@end
MoleView.mにプログラムを追加
MoleView.mのsetupメソッドには、UIImageでモグラの穴の画像を読み込み、それをもとにUIImageViewを作成し、自身のViewに配置しました。
今回はそのあとに、さらにモグラのアニメーション用のUIImageViewを追加します。
- (void)setup {
UIImage *image = [UIImage imageNamed:@"hole.png"];
UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
imageView.frame = self.bounds;
[self addSubview:imageView];
// ここから追加
moleImageView = [[[UIImageView alloc] init] autorelease];
moleImageView.frame = self.bounds;
[self addSubview:moleImageView];
moleImageView.animationImages =
[NSArray arrayWithObjects:[UIImage imageNamed:@"mole1.png"],
[UIImage imageNamed:@"mole2.png"],
[UIImage imageNamed:@"mole3.png"],
[UIImage imageNamed:@"mole4.png"],
[UIImage imageNamed:@"mole5.png"],
[UIImage imageNamed:@"mole6.png"],
[UIImage imageNamed:@"mole5.png"],
[UIImage imageNamed:@"mole4.png"],
[UIImage imageNamed:@"mole3.png"],
[UIImage imageNamed:@"mole2.png"],
[UIImage imageNamed:@"mole1.png"],nil];
moleImageView.animationDuration = 1.0;
[moleImageView startAnimating];
// ここまで追加
}
ビルドして実行結果を見る
プロジェクトメニューからビルドと実行を選択して、プログラムをビルド実行してみましょう。
iPhone Simulatorが起動してMoleAttackアプリが実行されます。
左上のモグラが出たり入ったりするアニメーションをご覧いただけると思います。
プログラムの解説
今回追加したプログラムを見ていきましょう。
まず追加したプログラムの最初の3行ですが、これはモグラの穴のUIImageViewと同じです。
moleImageView = [[[UIImageView alloc] init] autorelease];
moleImageView.frame = self.bounds;
[self addSubview:moleImageView];
まず、1行目でUIImageViewを生成し、2行目でバウンダリ座標を自身と同じに設定します。
最後に自身のビューの中に生成したUIImageViewを配置します。
UIImageViewを生成して配置したので、次に行うことはアニメーション画像の登録です。
その部分が以下のプログラムになります。
moleImageView.animationImages =
[NSArray arrayWithObjects:[UIImage imageNamed:@"mole1.png"],
[UIImage imageNamed:@"mole2.png"],
[UIImage imageNamed:@"mole3.png"],
[UIImage imageNamed:@"mole4.png"],
[UIImage imageNamed:@"mole5.png"],
[UIImage imageNamed:@"mole6.png"],
[UIImage imageNamed:@"mole5.png"],
[UIImage imageNamed:@"mole4.png"],
[UIImage imageNamed:@"mole3.png"],
[UIImage imageNamed:@"mole2.png"],
[UIImage imageNamed:@"mole1.png"],nil];
UIImageViewの持つanimationImagesプロパティは、NSArray型(配列)の変数です。
つまり、アニメーションの画像のNSArrayをこれに与えると、UIImageViewが順々に表示してくれるしくみです。
画像はUIImageクラスのimageNamedメソッドで、プロジェクトに登録した画像を読み込みます。
mole1からmole5までいったあと、mole1まで戻ります。このように登録することで、モグラが飛び出して穴に引っ込むようなアニメーション効果が得られます。
最後にnilを指定してターミネーションしています(終わりを意味する重要なキーワードです)。
アニメーションの画像の登録が終わったら、今度はアニメーションの時間を指定します。
moleImageView.animationDuration = 1.0;
UIImageの持つanimationDurationプロパティに、全体の再生に要する時間を秒単位で指定します。
ここでは、1.0という値を与えているので、モグラが飛び出して引っ込むまでの時間が1秒ということになります。
秒数の指定をかえるとアニメーションの速度が変わりますので、試してみてください。
最後に、UIImageViewのstartAnimatingメソッドを実行すると、アニメーションが始まります。
[moleImageView startAnimating];
アニメーションの設定が終わっても、これを忘れるとアニメーションが始まりません。
また、stopAnimatingメソッドを実行すると、アニメーションを停止させることもできます。
今回はここまで。次回はモグラの数を3×3まで増やし、ランダムに出入りするようにプログラムを追加します。
iPhoneではじめるサンデープログラミング (16)
今回は、前回追加したプログラムについて解説します。
配置したビューの階層構造
前々回(第14回)では、Interface Builderでもともとあるビュー(下図の赤色のUIView)の中に、ビュー(緑色のUIView)を追加しました。さらにその中に、ビュー(青色のUIView)を追加してClassをMoleViewに設定しました。
前回は、緑色のUIViewは、MoleAttackViewControllerにUIView型でOutlet宣言したgameViewと接続しました。これは、MoleAttackViewController.mで緑色のUIViewにアクセスするためです。
青色のMoleViewはOutlet接続していませんが、これはgameViewの中に配置したので、Outlet接続したgameViewを介してアクセスできます。
MoleView
モグラの画像を表示するためのクラスとしてMoleViewを作成しました。
MoleViewはUIViewのサブクラスです。画面表示を目的としたクラスを作成する場合、それに必要なメソッドやプロパティを備えたUIViewをベースにすることはiPhone OSの基本です。
MoleView.hには、setupというメソッドを定義しました。
@interface MoleView : UIView {
}
- (void)setup; //追加行
@end
この定義に対する実装をMoleView.mに書き加えました。
- (void)setup {
UIImage *image = [UIImage imageNamed:@"hole.png"];
UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
imageView.frame = self.bounds;
[self addSubview:imageView];
}
setupメソッドの中で何をやっているかを解説します。
まず1行目では、画像データをハンドリングするクラスであるUIImageを作成しています。UIImageクラスが持つ、imageNameというクラスメソッドを使うと、プロジェクトに登録した画像ファイル名を指定して、UIImageのインスタンスを作成できます。
2行目では、画像を表示するためのクラスであるUIImageViewを作成しています。1行目で作成したUIImageを引数として与え、その画像を表示するようにしています。
3行目では、2行目で作成したUIImageViewのバウンダリ(表示領域)を設定しています。2行目ではバウンダリが確定していないので、自身のバウンダリと同じ大きさに設定します。
4行目で自身のビューの中に、UIImageViewを追加しています。これを忘れると、せっかくUIImageViewを作ってもビューが廃止されないため、画面上に表示されないことになります。
以上のメソッドで、自身のビューの中に画像を表示するUIImageViewを作り、その画像としてプロジェクトに登録した「hole.png」を設定しています。
setupメソッドを呼ぶループ
では、MoleViewクラスに実装したsetupメソッドは、どこで呼ばれるのでしょうか?
それは、MoveAttackViewController.mのviewDidLoadメソッドの中です。
viewDidLoadメソッドは、ViewControllerがメモリのロードされ、ビューが表示される直前に呼ばれます。
ここで、gameViewの中にあるすべてのMoleViewに対して、setupメソッドを実行するようなforループを書きます。
- (void)viewDidLoad {
[super viewDidLoad];
//追加行
for ( MoleView *view in gameView.subviews )
{
[view setup];
}
//ここまで
}
このforループは、Objective-C 2.0から採用されたイテレータループで、配列オブジェクト(NSArray)のすべてのオブジェクトを順次取り出せる便利なループ構文です。
for ( 変数の型 変数 in 配列オブジェクト )
UIViewにsubviewsというNSArray型のプロパティがあり、これを使うことでそのビューの中にあるすべてのビューオブジェクトにアクセスできます。ここでは、gameView.subviewsと指定することで、MoleView型で宣言したviewにそのオブジェクトが入り、[view setup]でsetupメソッドを実行しています。
MoleViewを直接、ViewControllerにもともとあるビューの中に配置せず、gameViewというUIViewをコンテナとして配置し、その中にMoleViewを置いた理由はここあります。
今後、様々なビューオブジェクトを配置しますが、同じクラスのオブジェクトは、アクセスし易いように一塊にビューでグルーピングすることで、プログラムをシンプルにできると考えています。
今はMoleViewをひとつだけしか配置していませんが、gameViewの中のすべてのMoleViewに対して、setupメソッドを実行するようにプログラムを書きましたので、あとはMoleViewがいくつあってもソースコードを書き換えること無く動作します。そこが、オブジェクトの良さであると言えますし、仕様の変更に柔軟に対応できるプログラム設計がiPhoneプログラマには求められているのだと思います。
今回はここまで。次回はモグラをアニメーション表示するプログラムを作成します。
iPhoneではじめるサンデープログラミング (15)
前回はInterface BuilderでMoleAttackViewControllerのメインビュー上に、さらにビュー(UIView)を配置しました。
今回は、Xcodeで配置したビューに対するプログラムを入力し、画像を表示させることに挑戦します。
MoleAttackViewController.hにプログラムを追加する
Xcodeに切り替えて、プロジェクトウインドウの左側にあるリストから、MoleAttackViewController.hをクリックします。
プロジェクトウインドウ右下のエディタにMoleAttackViewController.hの内容が表示されます。
以降、「xxxxxをエディタに表示させる」という説明は、この操作を意味しますので覚えてください。
ヘッダファイルにgameViewという名前でUIView型のメンバー変数を宣言します。その際、Outlet接続するためにIBOutletを付加します。
@interface MoleAttackViewController : UIViewController {
IBOutlet UIView *gameView; //追加行
}
@end
UIViewとgameViewをOutlet接続する
今度はInterface Builderに切り替えて、MoleAttackViewController.xibを表示させます。
プロジェクトウインドウから、MoleAttackViewController.xibをダブルクリックしても結構です。
File’s Ownerというアイコンから前回追加したUIView(最初に追加した320×440の大きいビュー)に向かって、controlキーを押しながらドラッグします(マウスの右ボタンでもOK)。
ドラッグが終わると、小さい黒ウインドウが表示されるので、「gameView」を選択します。
MoleAttackViewController.mにプログラムを追加する
またXcodeに切り替えて、今度はMoleAttackViewController.mをエディタに表示させます。
まずは先頭で、MoleViewのヘッダをインポートします(MoleViewクラスは後ほど作成します)。
#import "MoleAttackViewController.h"
#import "MoleView.h" //追加行
次に、すでに入力されているviewDidLoadメソッドのコメントを外します(/*と*/を消す)。
gameView内のすべてのMoleViewオブジェクトに対して、setupメッセージを実行するループ文を追加します。
- (void)viewDidLoad {
[super viewDidLoad];
//追加行
for ( MoleView *view in gameView.subviews )
{
[view setup];
}
//ここまで
}
MoleViewクラスの作成
モグラを表現するクラス(MoleView)を作成します。MoleViewは、UIViewをベースとしたサブクラスで、表示の他にタッチによるユーザ入力の役割も果たします。
「ファイルメニュー」から「新規ファイル…」を選択すると、ファイルを作成するためのテンプレートダイアログが表示されます。
「Objective-C Class」を選択して、「Subclass of」のポップアップは「UIView」を選択します。これはUIViewをベースクラスとすることを意味します。最後に「次へ」ボタンを押します。
ファイル名に「MoleView.m」と入力して、「完了」ボタンを押します。
「同時に”MoleView.h”も作成」にチェックが入っていることも確認してください。
「MoleView.m」と「MoleView.h」がClassesグループに追加されました。
もし他のグループ内に作成された場合は、ドラッグしてClassesグループ内に移動してください。
MoleView.hにプログラムを追加する
MoleView.hをエディタに表示させて、setupメソッドの宣言文を追加します。
@interface MoleView : UIView {
}
- (void)setup; //追加行
@end
MoleView.mにプログラムを追加する
MoleView.mをエディタに表示させて、以下のsetupメソッドの実装コードを追加します。
- (void)setup {
UIImage *image = [UIImage imageNamed:@"hole.png"];
UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
imageView.frame = self.bounds;
[self addSubview:imageView];
}
UIViewをMoleViewに変更
Interface Builderに切り替えて、前回配置したUIView(80×80の小さいビュー)をMoleViewに変更します。
配置したUIViewを選択して、Inspectorパレットの上部にあるタブボタンの一番右を押して、表示モードを切り替えます。
Class IdentityのClassポップアップボタンから「MoleView」を選択します。
これで配置したUIViewがMoleViewオブジェクトに変わります。
ビルドと実行
Xcodeに戻ってビルドして実行すると、iPhone Simulatorが起動しアプリが実行されます。
スクリーン上には、モグラの穴の画像が表示されるはずです。
今回はここまで。次回は、今回入力したソースコードを解説します。
ここまでのプロジェクト一式はここからダウンロードできます。
iPhoneではじめるサンデープログラミング (14)
今回はInterface Builderでゲーム画面をデザインします。
デザインというとちょっと言葉が大袈裟ですが、画面上に表示するためのUIViewクラスを配置し、その色を設定するところまでを行います。
ビューの背景色を変更する
Xcodeはソースファイルの管理、ソースコードの編集等、プログラミングの大半を行うためのツールです。一方、Interface Builderは、画面デザインを主目的としたグラフィカルな操作が中心のツールです。画面デザイン以外にも、OutletやActionといった、ソースコードとの関連付けをする重要な役割もありますが、Xcodeと比べてドローイングソフトのような印象があるので、とっつきやすいと思います。
まずは、Xcodeのプロジェクトウインドウの左側にある「グループとファイル」リストから、Resourcesグループの中にある「MoleAttackViewController.xib」をダブルクリックします。
Interface Builderが起動し、画面にウインドウやパレットが表示されます。
まず、「MoleAttackViewController.xib」というタイトルのウインドウを見つけてください。これは、Xcodeで言うところのプロジェクトウインドウに相当するウインドウで、xibファイルを構成するオブジェクトが表示されます。
ウインドウ左上のViewModeから3種類の表示形式を選択できます。Finderの表示モードと同様、アイコン、リスト、カラムから選べます。
その中から「View」をダブルクリックすると、ビューの実体が別ウインドウで表示されます(このウインドウが実際にiPhone上に表示されます)。
InspectorパレットでViewの背景色を茶色に変更しましょう(モグラたたきなので、土をイメージして)。
パレット上部にある一番左のタブボタンを押して、Backgroundの右のボタンを押すと、Colorsパレットが表示されます。
コンテナビュービューの追加
ビューの中にもうひとつビューを配置します。
これはモグラを表示するためのビューをひとまとめにするためのビューです。
ビューは何かを表示する以外にも、複数のビューを束ねる役目もあります(この連載ではコンテナビューと呼びます)。
コンテナビューがなくても画像は表示できますが、ビューの管理をやりやすくするために作ります(その効果は後にわかりますので、ここでの解説は割愛します)。
Libraryパレットのオブジェクトグループリストから、[Library] → [Cocoa Touch Plugin] の階層をたどって「Windows, Views & Bars」グループを選択します。オブジェクトリストからUIViewを探したら、ウインドウの上にドラッグ&ドロップします。
Inspectorパレットで、配置したUIViewの位置と大きさを変更します。
パレット上部にある右から2番目のタブボタンを押すと、Size & Positionという項目で位置と大きさを数値指定できます。
つぎにInspectorパレットで、Backgroundの色を透明にします。
これは、背景にあるビューを表示させるための設定です。
ColorsパレットのOpacityスライダーを一番左(0%)にすると透明に設定できます。
モグラを表現するためのビューをさらに追加
いま配置したビューの中に、さらにビューを配置します。
このビューが後にモグラを表示したりタッチに反応するためのMoveViewとなります。
LiblaryパレットからUIViewを探して、先ほど配置したビューの中に追加します。
場所は適当でいいのですが、大きさは80×80にします。
Inspectorパレットで、Backgroundの色を透明にします。
以上の操作でモグラを1匹表示するための準備が終わりました。
整理すると以下のようになります。
次回は配置したビューに対してプログラムを書き、さらにモグラを表現するためのクラス(MoleView)を新規に作成します。
iPhoneではじめるサンデープログラミング (13)
今回からモグラたたきゲームの制作に入ります。
まずは新規にプロジェクトファイルを作成し、ゲームに必要な画像をプロジェクトに登録するところまで行います。
前シリーズで学んだカウンタアプリの制作と同じ手順ですので、復習も兼ねて進めていきましょう。
プロジェクトの作成
まずはXcodeを起動します。
Xcodeは、iPhone SDKをインストールするとハードディスクの最上階層に出来上がるDeveloperフォルダの中の、Applicationsフォルダに入っています。
上の画面では、「Xcode.app」というようにアプリ名に拡張子が表示されています。これは、Finderの環境設定で「すべてのファイル拡張子を表示」にチェックを入れているためです。この連載ではすべての画面に対して、この設定を前提に解説しています。
Xcodeが起動したら、まずは新規のプロジェクトを作成します。
プロジェクトとは、プログラムの開発に必要なファイルやリソースを包括的に管理するためのもので、通常ひとつのアプリケーションごとに、1プロジェクトとして作成します。
「プロジェクトを作る」、「プロジェクトをビルドする」というように、ひとつのアプリケーションに対してこの用語が使われます。
では、ファイルメニューから「新規プロジェクト…」を選択しましょう。
新規プロジェクトのテンプレートを選択するダイアログが表示されます。
左側のリストからiPhone OSのApplicationをクリックし、右側に表示されるアイコン群から「View-based Application」を選択し、最後に右下の「選択…」ボタンを押します。
カウンタアプリの時は、Window-based Applicationを選択しましたが、これは単一のビューで完結するようなアプリに適したテンプレートです。今回は、複数のビューを組み合わせますので、View-based Applicationをもとに作成します。
プロジェクトフォルダ(プロジェクトで使用するファイル群が入っているフォルダ)の保存先を指定するためのシートダイアログが表示されます。
今回は名前を「MoleAttack」、保存先を「デスクトップ」と指定し、 保存ボタンを押します。
以上の操作で新規にプロジェクトが作成され、画面にプロジェクトウインドウが表示されます。
画像の登録
ゲームで使用する画像ファイルをプロジェクトに登録します。
画像は透過したPNGフォーマットで作成します。
Photoshop等の画像編集ソフトを使うと、透過情報を持ったPNGファイルを容易に作成できます。
作成した複数の画像は、扱いやすいようにひとつのフォルダにまとめると便利です。
今回はImagesというフォルダを作って、その中に画像ファイルを収めています。
プロジェクトフォルダの中にImagesフォルダを移動します。
なお、モグラの画像の入ったImagesフォルダは、ココをクリックするとダウンロードできます。
作成するアプリで画像を使用するために、プロジェクトに画像を登録します。
プロジェクトウインドウのResourcesグループの中に、FinderからImagesフォルダをドラッグ&ドロップします。
以上の操作で画像の登録が完了しました。
次回は、Interface Builderを使ってゲーム画面をデザインしていきます。
iPhoneではじめるサンデープログラミング (12)
今回から新しいシリーズをスタートします。
前のシリーズではカウンタのアプリを作成しましたが、もう少し面白いアプリに挑戦します。
宿題の回答
その前に前回の宿題の回答から。
前回はリセットボタンを追加する宿題を出して終わりました。
皆さんうまく追加できたでしょうか?
まず、ヘッダファイル(CounterAppDelegate.h)に「resetButtonPushed」という名前でアクションメソッドを宣言します。Countボタンを押したときの処理と同じ要領です。
@interface CounterAppDelegate : NSObject {
UIWindow *window;
IBOutlet UILabel *label;
}
- (IBAction)countButtonPushed:(id)sender;
- (IBAction)resetButtonPushed:(id)sender; // 追加行
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
ヘッダにメソッドを宣言したら、Interface Builderに切り替えてボタンを配置しましょう。
LibraryパレットからUIButtonを見つけて、スクリーン上にドラッグ&ドロップします。
ボタンをダブルクリックすると、タイトルを変更できますので「Reset」とタイプします。
また、押しやすいようにボタンの隅をドラッグして横幅を拡げます。
次に、controlキーを押しながら、配置したボタンの上でマウスクリックし、Counter App Delegateのサイコロアイコンまでドラッグします。アイコンの上でマウスボタンを離すと「Events – resetButtonPushed:」と表示されますので、「resetButtonPushed:」を選択します。
以上の操作でAction接続が完了しました。
もう一度Xcodeに戻って、CounterAppDelegate.mを開きます。
以下のソースコードを打ち込んで、ボタンが押されたときの処理を実装します。
- (IBAction)resetButtonPushed:(id)sender {
[label setText:@"0"];
}
ビルドメニューから「ビルドと実行」を選択して、プログラムを動かしてみましょう。
アプリが起動したら「Reset」ボタンをクリックしてください。
UILabelの数字が「0」に戻るはずです。
新シリーズのアプリ
カウンタアプリの制作でXcodeとInterface Builderの基本的な使い方、OutletとActionのしくみ、アイコンやローカライズの設定方法など、iPhoneアプリの作り方を一通り解説しました。
今度は「モグラたたき」ゲームに挑戦します。iPhoneの画面とタッチして、飛び出してくるモグラを叩くという反射神経型ゲームです。
なぜ「モグラたたき」か?という問いに明確な理由はありませんが、何か簡単なゲームを作ることは取っつきやすいですし、楽しいテーマなので継続してプログラミングできると思います。
サンデープログラミングというタイトルの連載ですが、今度のテーマはかなり実践的な内容です。
Objective-CのソースコードやUIクラスが数多く登場し、まったくの初心者の方は面食らうことが多々あるかと思います。
しかしながら、一本のアプリケーションをゼロから作り上げていく過程をひと通り経験することは、今後iPhone SDKを学習する上で大きく生かされると思っております。
今は理解できなくても、これからの学習によって積み上げていく知識や経験により、自分の力で租借できるようになる日がくるでしょう。
前回までのカウンタアプリでは、Cocoa TouchフレームワークはUILabel、UIButton、NSNumber程度しか登場しませんでしたが、今度はUIView、UIImageViewなどによる画像表示や、NSTimerで定期的にメソッドを動作させてリアルタイムでゲームを進行させる方法を解説します。
今回はここまで。次回から新規のプロジェクトを作成し、ゲームの制作に取りかかります。
iPhone SDKではじめるサンデープログラミング (11)
11回目を迎えるこのシリーズも今回が最後です。
最後はアプリ名のローカライズとリセットボタンの追加です。
アプリ名のローカライズ
ホーム画面に表示されるアプリ名は、プロジェクト名と同じ「Counter」になっています。
これは、どの言語環境においても同じです。
そこで、アプリ名のローカライズをしてみましょう。
まず、プロジェクトにInfoPlistファイルを追加します。
プロジェクトウインドウの左側にあるリストから、Resourcesグループを見つけてください。
その上でマウスの右クリック(またはcontrolキーを押しながら)し、コンテキストメニューが表示されたら「追加」→「新規ファイル…」を選択します。
新規ファイルの作成ダイアログが表示されるので、カテゴリリストから「Other」を選択し、「Strings File」を選択して「次へ」ボタンを押します。
ファイル名のフィールドに「InfoPlist.string」と入力して、「完了」ボタンを押します。
Resourcesグループに「InfoPlist.strings」が追加されます。
「InfoPlist.strings」を選択して、プロジェクトウインドウの「情報」ボタンを押します。
「一般」タブをクリックして、「ファイルをローカライズ可能にする」ボタンを押します。
もう一度「一般」タブをクリックして、「ローカリゼーションを追加」ボタンを押します。
ポップアップボタンから「Japanese」を選択して追加ボタンを押します。
ローカリゼーションリストに「Japanese」が追加されました。
プロジェクトウインドウの「InfoPlist.strings」を見ると、左に三角形が付いていることがわかります。
三角形をクリックするとサブアイテムが表示され、それぞれ「English」と「Japanese」になっています。
それぞれのファイルを開いて、文字列のソースコードを入力します。
アプリをビルドして実行したら、前回と同様に言語環境を切り替えて、正しくアプリ名がローカライズされているかを確認しましょう。
リセットボタンの追加
このシリーズで作成したカウンタアプリは、カウントアップした後ゼロに戻す機能がありません。
アプリを終了させればゼロに戻りますが、戻したいときに毎回終了させるのは不便です。
そこで、リセットボタンの機能を実現するプログラムを追加してみましょう。
…っと、本来ならばここで解説するのですが、最後はいままでの復習も兼ねて宿題にしたいと思います。
OutletとActionのしくみを理解していれば、カウントボタンを実装したことと同様に、リセットボタンを追加するだけです。
答えは次回の新シリーズの最初に解説いたしますが、リセットボタンを実装する上でのヒントを挙げて、このシリーズを終わりたいと思います。
いままでご愛読いただき、ありがとうございました。
リセットボタン実装のヒント
[label setText:@"0"];
iPhone SDKではじめるサンデープログラミング (10)
今回はアイコンの設定とローカライズのお話です。
プログラミングとはちょっと離れますが、両者ともiPhoneアプリの開発には欠かせない要素です。
アイコンは、アプリケーションの顔とも言えますので、そのデザインによって見栄えと印象が大きく変わるでしょう。
ローカライズは、複数の言語をサポートする上で必須な作業です。世界に発信するアプリを目指すならば、多くの言語をサポートしたいものです。
アイコンを付ける
アイコンを設定していないiPhoneアプリはのっぺらぼうです。
まず、アイコンとなる画像を作成します。
画像フォーマットはPNGで、57×57ピクセルの大きさで作成するルールになっています。
Photoshop等のビットマップ編集アプリを使って「Icon.png」というファイル名でプロジェクトフォルダの中に保存します。
アイコンのデザイン力はプログラミングのスキルとは無関係です。
自分のデザイン力のなさを改めて痛感します(^ ^;)
背景をアルファチャンネルで作成すると、その部分が透過して表示させることができます。
画像ファイルを作成したら、FinderからプロジェクトウインドウのResourcesグループにドラッグ&ドロップします。
アイコンはプロジェクトをビルドして実行すると入れ替わります。プロジェクトに追加しただけではアップデートされません。
反射効果をやめる
アイコンには、画像に光が反射するような効果が自動的に付加されます。
もし、この効果が邪魔な場合は、Counter-Info.plistを編集することで反射を無効にできます。
Counter-Info.plistは、アプリに関する設定を行うためのプロパティリスト形式(XML)のファイルです。
プロジェクトウインドウのリストのResourcesグループから「Counter-Info.plist」を選択して、右下のリストの一番下の項目を選択すると、右側に「+」ボタンが表示されます。
このボタンを押すと、リストの一番下に項目が追加されます。
ポップアップボタンから「Icon already includes gloss and bevel effects」を選択して、右側のチェックをオンにします。
アプリをビルド実行して、反射効果が消えているかを確認してください。
ローカライズ
今度はローカライズの作業です。
iPhoneアプリは、各言語用のxibファイルを用意することで、アプリ起動時に言語環境に応じた適切なxibファイルがロードされる仕組みを持っています。
現在はxibファイルがひとつなので、言語環境の設定にかかわらず、同じ画面が表示されます。
そこで、英語の場合は英語の画面、日本語の場合は日本語の画面、といったように、言語環境に合わせて画面が切り替わるように日本語のxibファイルを追加します。
プロジェクトウインドウのResourcesグループにある「MainWindow.xib」を選択して、プロジェクトウインドウの情報ボタンを押します。
一般タブをクリックして、「ファイルをローカライズ可能にする」ボタンを押します。
もう一度一般タブをクリックして、「ローカリゼーションを追加」ボタンを押します。
ポップアップボタンから「Japanese」を選択して追加ボタンを押します。
プロジェクトウインドウの「MainWindow.xib」を見ると、左に三角形が付いていることがわかります。
三角形をクリックするとサブアイテムが表示され、それぞれ「English」と「Japanese」になっています。
Japaneseをダブルクリックすると、日本語版のxibファイルがInterface Builderで開きます。
これは英語版のxibファイルをコピーしたものなので、ボタンの名称は「Count」のままです。これを「カウント」に書き換えます。
xibファイルの内容はローカリゼーションを追加したときにコピーされます。一度分割されたxibファイルは、各言語ごとに独立しているので、内容はリンクしていません。例えば、日本語のローカリゼーションを追加したあとにボタンを増やした場合は、英語のxibファイルも手動でボタンを追加する必要があります。多くのコントロールを持つアプリの場合は、画面デザインがフィックスした後にローカライズした方が効率的と言えます。
iPhone Simulator上で【設定】→【一般】→【言語環境】→【言語】を選択そ、言語を日本語や英語に切り替えて、正しくその言語の表記がされるかチェックしましょう(英語の場合は「Count」、日本語の場合は「カウント」になるかどうか)。
言語環境を切り替えても、うまくその言語の表記にならない場合は、xibファイルが正しくビルドされていない可能性があります。そのようなときは、ビルドメニューから「クリーニング」を選択して、アプリを再ビルドしてみてください。
今回はここまで。次回はアプリ名のローカライズとリセットボタンの追加を行います。
iPhone SDKではじめるサンデープログラミング (9)
前回までのステップで、カウンターの機能はひと通り実装しました。
とても単純な機能しかありませんが、ユーザからのアクションに応じて、スクリーン上のオブジェクト(今回の例ではUILabel)が変化するアプリを作りました。
その制作過程で、OutletとActionという重要な仕組みを学習しましたが、今回は復習も兼ねてOutletとActionの接続状況を確認する方法を解説します。
Outletの接続状況
プロジェクトウインドウから「MainWindow.xib」をダブルクリックします。
Interface Builderに切り替わりますので、ウインドウ上に配置されたラベル(UILabel)をクリックします。
つぎに、Inspectorパレットの上部にあるタブボタンの、左から2番目をクリックします。このタブは、選択されたオブジェクトのOutletやActionを確認するためのモードです。
※Inspectorパレットが表示されていない場合は、Toolメニューから「Inspector」を選択してください。
パレットに選択されたオブジェクトのOutlet接続が確認できます。
この場合、label(UILabel)はCounter App Delegateに接続されていることがわかります。
接続を解除するには、Counter App Delegateの左にある×ボタンを押します。
今度は、MainWindow.xibというタイトルのウインドウ上にある、Counter App Delegateのアイコンをクリックします。
すると、InspectorパレットにOutlet接続されたオブジェクトがリスト表示されます。
つまり、接続された両方のオブジェクトに対して、Inspectorパレットは状況を確認できるようになっています。
なお、オブジェクトの上でマウスの右ボタン(もしくはcontrolキーを押しながらクリック)を押すと、Inspectorパレットと同様の情報が確認できます。
場合によってはこちらの方法が即座に表示できるので便利でしょう。
Outletの接続は、マウスドラッグで簡単に設定できますが、あとから全体を視覚的に俯瞰することはできません。Outletの設定が正しくない理由でアプリが落ちたり、おかしな挙動になるケースが多々あります。作ったアプリのバグはソースコード以外にも潜んでいますので、バグが取れないときは、Interface Builderでの設定も疑う必要があります。
Actionの接続状況
ActionもOutletと同様の操作で確認できます。
ウインドウ上に配置されたボタンをクリックすると、Inspectorパレットに接続されたメソッドが表示されます。
この場合、Touch Up Insideというアクションの時に、countButtonPushedというメソッドが実行されることが確認できます。
Touch Up Insideとは、ボタンの上をタップして、そのままボタンの内部でタップを終了(指を離す)するアクションです。
他にもボタンに関して様々なアクションが定義されています。
例えば、Touch Downというアクションに接続すると、ボタンをタップした瞬間にcountButtonPushedが呼ばれます。
試しに、Touch Downの右にある●アイコンから、Counter App Delegateのアイコンに向かってドラッグすると、そのアクションが接続されます。
つぎに、Touch Up Insideの×ボタンを押して接続を解除してください。
2つのアクションを接続することもできますが、そうするとひとつのタップでメソッドが2回呼ばれてしまうので、今回はTouch Up Insideを解除します。
これでアプリを実行すると、いままではタップして指を離した瞬間に、countButtonPushedが呼ばれて、数字がカウントアップされましたが、今度はタップした瞬間にcountButtonPushedが実行されてカウントアップします。
ボタンから直接、Counter App Delegateのアイコンにドラッグすると、Touch Up Insideのアクションで接続されますが、Inspectorパレットのリスト上にある●アイコンからドラッグすると、様々なアクションと接続できるわけです。
カウントアップのしくみ
countButtonPushedメソッドには、スクリーン上のラベルの数字をカウントアップさせるプログラムが書いてあります。
- (IBAction)countButtonPushed:(id)sender {
int counter = label.text.intValue;
counter++;
NSNumber *number = [NSNumber numberWithInt:counter];
[label setText:number.stringValue];
}
わずか4行ですが、Objective-C言語で書かれたこのプログラムを読み解いてみましょう。
まず、1行目ですが、init型(整数型)の変数counterに、ラベルの数値を代入しています。
label.text.intValueとは、Outlet接続したlabelのtextプロパティから、intValueで整数を取得することを意味します。
int counter = label.text.intValue;
2行目は、++演算子を使って、変数counterに1を足しています。
ounter++;
3行目では、変数counterをもとに、NSNumberオブジェクトを生成しています。
NSNumberとは、数値を表現するためのクラスで、今回は数値からNSString型(文字列型)を取得するために使います。
NSNumber *number = [NSNumber numberWithInt:counter];
4行目では、labelに対してsetTextメソッドを呼んで、パラメータにNSNumberから得た文字列(number.stringValue)を与えています。
[label setText:number.stringValue];
以上の手順で、ラベルから数値を取得し、その数値に1を足し、その値をもう一度ラベルにセットしています。
NSStringやNSNumberなどの文字列や数値を扱うクラスは「ラッパークラス」と呼ばれ、iPhoneアプリの開発では使う機会が非常に多い重要なクラスです。参考図書やネット上の情報をもとに、使い方をマスターすることが、iPhoneアプリ開発のスキル向上につながるでしょう。
次回はプログラムコードとはちょっと離れて、アイコンとローカライズについて解説します。
iPhone SDKではじめるサンデープログラミング (8)
今回はユーザの入力に反応するためのアクションについて解説します。
スクリーン上に配置したボタンやスライダーなどのコントロールは、ユーザからの操作によってイベントが発生し、指定したメソッドを実行できます。
前回ではOutletという仕組みでソースコードとInterface Builderのオブジェクトを接続しましたが、アクションもほぼ同様の操作で関連性を設定します。
■ヘッダでのメソッド宣言
まず、ヘッダファイルにアクションによって実行されるメソッドを宣言します。
これをやらないと、Interface Builderでメソッドを認識できないので、先にヘッダに書き加えることが必須です。
メソッドはIBAction型を返り値とし、id型の引数をひとつ持つプロトタイプで宣言します。
今回は「countButtonPushed」という名前で、以下のようにCounterAppDelegate.hにメソッドを宣言します。
@interface CounterAppDelegate : NSObject {
UIWindow *window;
IBOutlet UILabel *label;
}
- (IBAction)countButtonPushed:(id)sender; // 追加行
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
なお、IBAction型はフレームワークのヘッダでvoidで定義されているだけなので、実際には返り値を実装する必要はありません。IBAction型を戻り値としたメソッドがInterface Builderで認識され、Actionとしてコントロールと接続できます。
#ifndef IBAction
#define IBAction void
#endif
id型の引数は、アクションが発生したオブジェクト(ボタンやスライダーなどのコントロール)が渡されるので、どのオブジェクトからのアクションかを判別することができます。
■Interface Builderでコネクション
ヘッダにメソッドを宣言したら、Interface Builderに切り替えてボタンを配置しましょう。
LibraryパレットからUIButtonを見つけて、スクリーン上にドラッグ&ドロップします。
ボタンをダブルクリックすると、タイトルを変更できますので「Count」とタイプします。
また、押しやすいようにボタンの隅をドラッグして横幅を拡げます。
次に、controlキーを押しながら、配置したボタンの上でマウスクリックし、Counter App Delegateのサイコロアイコンまでドラッグします。アイコンの上でマウスボタンを離すと「Events – countButtonPushed:」と表示されますので、「countButtonPushed:」を選択します。
以上の操作でAction接続が完了しました。
最後に、UILabelの文字列を「0」に変更してください。これはカウンターの初期値となり、実行したときに0からカウントアップさせるためです。
■メソッドの実装
もう一度Xcodeに戻って、CounterAppDelegate.mを開きます。
以下のソースコードを打ち込んで、ボタンが押されたときの処理を実装します。
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after application launch
[window makeKeyAndVisible];
}
- (void)dealloc {
[window release];
[super dealloc];
}
// 以下のソースコードを追加
- (IBAction)countButtonPushed:(id)sender {
int counter = label.text.intValue;
counter++;
NSNumber *number = [NSNumber numberWithInt:counter];
[label setText:number.stringValue];
}
■動作テスト
ビルドメニューから「ビルドと実行」を選択して、プログラムを動かしてみましょう。
アプリが起動したら「Count」ボタンをクリックしてください。
UILabelの数字がクリックするごとにひとつずつカウントアップするはずです。
次回は、countButtonPushedメソッド内に書いたプログラムの解説と、もう一度OutletとActionについておさらいします。
iPhone SDKではじめるサンデープログラミング (7)
前回はOutlet(アウトレット)と呼ばれるiPhoneアプリ開発にとって重要な仕組みについて解説しました。
Outletは、Interface Builderで配置したオブジェクトのメソッドを実行するために不可欠であるため、ここを十分に理解することが大切です。
今回は復習の意味も兼ねて、配置したUILabelのテキストが変更されるプログラムを解説します。
■Outlet接続
ヘッダファイルのメンバー変数宣言で「IBOutlet」と先頭に記すと、その変数はOutletとしてInterface Builderで認識されます。
ソースコード側から、Interface Builderで配置されたオブジェクトにアクセスするには、IBOutletというキーワードを使ってInterface Builderに接続する口を開けてあげるわけです。
メンバー変数のあるオブジェクトから、接続したいオブジェクトに向かってcontrolキーを押しながらマウスドラッグすると、Outlet宣言した変数がポップアップ表示され、接続したい変数名を選択すると接続は完了します。
■オブジェクトのメソッドを実行する
Outlet接続した変数に対しては、Xcodeのソースファイルにおいて、メソッドを実行することができます。
なお、Objective-C言語では「オブジェクトに対してメッセージを送る」といった表現を使いますが、私の連載ではメソッドを実行するという表現で統一しています。
前回のプログラムでは、setTextというメソッドでUILabelのテキストを指定した文字列(Hello)に変更しました。
setTextは、UILabelのテキストを変更するメソッドです。
テキスト以外にどのようなプロパティがあるかは、2種類の方法から調べることができます。
ひとつはリファレンスマニュアルです。これは、メソッド名の上でoptionキーを押しながらダブルクリックすると表示されます。内容は英語ですが、そのクラスの仕様が解説されています。
もうひとつはヘッダファイルを調べる方法です。メソッド名の上でcommandキーを押しながらダブルクリックすると、そのメソッドが宣言されているヘッダファイルが表示されます(複数のクラスに同一名のメソッドがある場合は、ポップアップで候補が表示されます)。
そこから、そのクラスの持つメンバー変数やメソッド、プロパティを読み解くことができます。
■property宣言とアクセッサ関数
メソッド以外にもドット構文を使って変数にアクセスすることができます。
先ほどのUILabelのsetTextの一文は以下のように書き表すことができます。
label.text = @"Hello"; // [label setText:@"Hello"]; と同じ
オブジェクトの後にドット、つぎに変数名を書くことで、ダイレクトに変数にアクセスできます。
もちろん、property宣言のオプションによってアクセスに制限がある場合は、それに従うことになります。
UILabelのヘッダファイルには、setTextというメソッドは宣言されていません。
宣言されていないメソッドが利用できる理由はproperty宣言にあります。
UILabelのtext変数はproperty宣言されているので、外部からアクセスするためのメソッド(アクセッサメソッド)が自動生成されます。
セットメソッド(代入メソッド)は、変数名の前に「set」が付き、そのあとの最初の1文字が大文字になります。
ゲットメソッド(取得メソッド)は、変数名がそのままメソッド名になります。
■UIApplicationのdelegateメソッド
ここまででOutlet接続やオブジェクトに対してメソッドを実行する方法が理解できたかと思います。
最後に、setTextメソッドを実行している「applicationDidFinishLaunching」について解説します。
このメソッドはUIApplicationクラスで定義しているメソッドで、アプリが起動したあと起動処理の最後のタイミングで呼ばれます。
つまり、アプリが起動してスクリーンが表示される前に実行したいプログラムをここに書くことになります。
他にもアプリが終了する直前など、いくつかのタイミング別にメソッドが定義されています。
今回は、アプリが起動する前に、UILabelオブジェクトのテキストを「Hello」に書き換えるため、applicationDidFinishLaunchingにsetTextメソッドを実装したわけです。
次回はアクションメソッドによるユーザからのボタンタップに対応します。
iPhone SDKではじめるサンデープログラミング (6)
今回はXcodeとInterface Builderの連携について解説します。
前回はInterface Builderでオブジェクトを配置したり、その属性を変更して画面をデザインしました。
しかし、それだけでは画面上のオブジェクトに対して、Xcodeで書いたプログラムは影響を与えることができません。
Xcodeで書いたプログラム(Objective-C)から、画面上のオブジェクトに対して命令を実行するにはOutlet(アウトレット)と呼ばれるしくみを使います。
■UILabelの配置
まずはInterface BuilderでUILabelを配置します。
UILabelとは、画面に文字列を表示することを目的としたオブジェクトです。
テキストフィールドのように、ユーザが文字を入力したり編集する機能はありませんが、フォントの大きさや色は自由に設定できます。
Libraryパレットから、UILabelを探してウインドウの上にドラッグ&ドロップします。
UILabelは、Libraryパレットの[Library]→[Cocoa Touch]→[Inputs & Values]カテゴリに入っています。
また、パレットの一番下にある検索フィールドにキーワードを入力すると、合致したオブジェクト名が絞り込み表示されます。
例えば、UILabelを探したい場合は「Label」と入力すると、簡単にUILabelをリストに表示できます。
■フォントの大きさを変更
UILabelを配置したら、フォントの大きさを変えてみましょう。
Inspectorパレットの上部にあるタブボタンから、一番左のボタンを押すと、パレットに配置したUILabelの属性が表示されます。
Fontの「Helvetica…」ボタンを押すと、フォントパネルが別に表示されますので、ここでフォントの大きさを設定できます。
もし大きくならない場合は、UILabelのバウンダリ(表示領域)を拡げてみてください。
■ヘッダファイルにコードを追加
Interface BuilderからXcodeに切り替えます。
プロジェクトウインドウの「CounterAppDelegate.h」をクリックすると、エディタビューにその内容が表示されます。
別ウインドウで大きく表示させたい場合は、「CounterAppDelegate.h」をダブルクリックしてください。
CounterAppDelegate.hはヘッダファイルと呼ばれ、クラスの中で使う変数やメソッドを定義します。
UIWindow…の下にOutletを宣言するための1行を書き加えます。
@interface CounterAppDelegate : NSObject {
UIWindow *window;
IBOutlet UILabel *label; // 追加行
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
■Outletの接続
今度はInterface Builderに切り替えます。
「MainWindow.xib」というタイトルのウインドウに、「Counter App Delegate」という黄色いサイコロのアイコンがあります。
このアイコンの上でcontrolキーを押しながらマウスドラッグすると、青い線が表示されます。
そのままウインドウの上のUILabelまで線を引っ張り、UILabelの上でマウスボタンを離します。
※2ボタンマウスをお使いの方は、controlキーを押す代わりに右ボタンのドラッグでも同様の操作ができます。
すると、黒い小さいウインドウに「label」と表示されますので、それを選択します。
これで、Outletの接続が完了しました。
■ソースファイルにコードを追加
再度Xcodeに切り替えます。
今度は「CounterAppDelegate.m」をクリックして、エディタビューに表示させます。
このファイルはソースファイルと呼ばれ、実際のプログラムコードを書くためのものです。
// Override point…の下に、UILabelに対してのプログラムコードを1行書き加えます。
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after application launch
[label setText:@"Hello"]; // 追加行
[window makeKeyAndVisible];
}
■プログラムのビルドと実行
入力が終わったらビルドして実行してみましょう。
プロジェクトウインドウの上部にある「ビルドして進行」ボタンを押します。
プログラムがコンパイルされ、必要なリソースやフレームワークがリンクされ、iPhoneアプリとしてファイルを生成します。
もしエラーがなければiPhoneシミュレータが起動して、画面に「Hello!」というラベルが表示さます。
エラーが表示された場合は、おそらく入力ミスの可能性が高いので、もう一度ヘッダファイルまたはソースファイルの入力した行を見直してください。
今回はここまでです。プログラムの入力とInterface Builderの操作で終わってしまいましたが、次回はこのプログラムの解説とOutlet接続のおさらいをします。
iPhone SDKではじめるサンデープログラミング (5)
今回はInterface Builderを使った画面デザインのお話です。
iPhoneアプリの使いやすさと美しさは、優れたユーザインターフェイスが支えています。
Interface Builderは、そのインターフェイスを設計するツールで、Xcodeとは別に独立したアプリとして提供されています。
■Interface Builderの起動
プロジェクトウインドウの左側リストのResourcesグループに「MainWindow.xib」というファイルがあります。
これがxibファイルと呼ばれるInterface Builderのファイルで、画面設計に関するデータがXML形式で記述されています。
【xibファイル】
これをダブルクリックするとInterface Builderが起動し、ウインドウやパレットが表示されます。
「MainWindow.xib」というタイトルのウインドウには、xibファイルを構成する4つのアイコンが表示されています。
【xibウインドウ】
その中の「Window」をダブルクリックすると、縦長のウインドウが表示されます(すでに表示されているかもしれません)。これがiPhoneのスクリーンになります。
【Window】
Interface Builderは、残念ながらメニューやウインドウが日本語化されていません。すべて英語表記なのですが、ドローイングソフトのような使い勝手なので、取っつきやすいと思います。今後のバージョンアップで日本語化されることを期待しています。
■Libraryパレット
Libraryというタイトルのパレットには、iPhoneアプリでよく見かけるボタンやスイッチ、ピッカーといった標準のコントロールの他、ビューやコントローラなどのオブジェクトが収められています。
これらを先ほどのウインドウ上にドラッグ&ドロップして、ユーザインターフェイスを設計していきます。
【Libraryパレット】
■Inspectorパレット
ウインドウに配置したオブジェクトは、Inspectorパレットで属性を変えることができます。
例えば、配置したUILabelオブジェクトを選択すると、InspectorパレットでUILabelに関する情報が表示されます。
フォントの大きさや位置揃えなど、ワープロソフトのように見た目を変更できます。
【Inspectorパレット】
■iPhoneシミュレータ
試しにLibraryパレットにあるボタンやスライダーなどをウインドウに並べてみましょう。
配置し終わったら、Fileメニューから「Simulate Interface」を選択します。
iPhoneシミュレータが起動し、オブジェクトを実際に動作させることができます。
【Simulate Interface】
動作といっても、単にボタンやスライダーがマウスクリックに反応するだけです。ユーザの入力に対して意味のある機能を提供するには、ソースコードの入力(Objective-Cによるプログラミング)が必須となります。
■ユーザインターフェイスガイドライン
ボタンやラベルなどのオブジェクトは自由に配置できますが、だからといってデタラメに画面を設計することは許されません。
質の高い操作性をすべてのアプリで実現するため、Appleはユーザインターフェイスに関するガイドラインを厳格に規定しています。
日本語に翻訳されたものがiPhoneの開発サイトからダウンロードできます。
132ページの読み応えのあるドキュメントですが、iPhoneアプリを開発するすべての方に必読の内容です。
ユーザインターフェイスの設計は、App Storeへ登録審査にも影響します。ガイドラインを無視したユーザインターフェイスの設計は、App Storeの審査時にReject(登録却下)される原因になります。円滑にApp Storeへアプリを登録する上で、ユーザインターフェイス設計への配慮は重要な要素と言えます。
次回はカウンタアプリに必要なユーザインターフェイスを設計し、Xcodeやソースコードとの連携について解説します。