Archive for 8月, 2010

ss100811_10.jpg

アプリが起動するところまで(3)

前回の記事の続きです。

今回はInterfaceBuilderで作成したUIと、XCodeで書いているコード部分をリンクさせます。
おおまかな作業内容は、

  1. AppDelegate_iPhoneクラスに、IBOutlet修飾子付きのプロパティを追加する
  2. InterfaceBuilderのInspectorウィンドウでプロパティのUI部品を関連づける

となります。ついでに起動時にリソース内のsamplebook.zipを、アプリのドキュメントフォルダにコピーする処理も書きます。

ss100811_10

 

■プロパティを定義する

AppDelegate_iPhoneクラスのヘッダファイルにプロパティの宣言を追加します。「@interface」~「@end」までがクラス宣言です(@interfaceはJavaなどで言うインターフェースとは別物です。Objective-Cでインターフェースに相当するものはプロトコルといいます)。

★AppDelegate_iPhone.h

#import <UIKit/UIKit.h>
#import "AppDelegate_Shared.h"

@interface AppDelegate_iPhone : AppDelegate_Shared {
	//メインのナビゲーションコントローラ(MainWindow_iPhone.xibで定義)
	IBOutlet UINavigationController* myNavigationController;
}

@property (nonatomic, retain) IBOutlet UINavigationController *myNavigationController;

@end

6行目が実際にデータを記憶するメンバ変数の定義、9行目がプロパティの宣言です。Objective-Cの型は基本的にC言語のポインタなのですね。

@propertyの後の「(nonatomic, retain)」はプロパティの属性の指定です。nonatomicはプロパティをスレッドセーフに「しない」指定だそうで、おそらくそのほうが速いとかそういうメリットがあるのでしょう。retainは、プロパティに代入するときに「オブジェクトのretainメソッドを呼び出すことを要求する」指定だそうです。

retainはObjective-Cのメモリ管理を行うメソッドで、オブジェクトの参照カウントを1増やします。参照カウントを減らしたいときはreleaseメソッドを呼び出し、カウントが0になるとオブジェクトは消滅します。理屈はそれほど難しくはないんですが、APIによっては中でretainやreleaseしているものがあるようで、その辺の癖がわかっていないとretainしすぎてメモリリークが起きたり、オブジェクトが消滅してしまっていてアプリが落ちたりすることもあります。

 

さて、プロパティの話に戻ります。他の言語と同じくプロパティに対してはsetterとgetterを書かなければいけないのですが、代入以外のことをしないのであれば、クラス定義の中に「@synthesize プロパティ名;」と書くと自動的に補完してくれます。

Objective-Cのクラス定義は「@implementation~@end」の間に書きます。

★AppDelegate_iPhone.m

#import "AppDelegate_iPhone.h"

@implementation AppDelegate_iPhone

@synthesize myNavigationController;

#pragma mark -
#pragma mark Application lifecycle

@synthesizeを使わない場合は、「プロパティ名」メソッド(getterになる)や「setプロパティ名」メソッド(setterになる)を書きます。メソッドの名前だけで判断しているようなので、うっかり書かないよう気を付けないといけませんね。

 

■UIとIBOutletプロパティを関連づける

IBOutlet付きのプロパティを定義したら、InterfaceBuilderに戻ってプロパティとUI部品の関連づけを行います。コードを修正しただけではInterfaceBuilder側でプロパティ名が出てこないことがあるので、一回「command+B」を押してビルドしたり、XIBファイルを閉じてXCodeから開き直したりするといいようです。

書類ウィンドウで「Navigation Controller」を選び、InspectorウィンドウのConnectionsタブ(左から2番目)をクリックします。そして、「New Referencing Outlet」の○を書類ウィンドウの「App Delegate」までドラッグします。

ss100811_14

プロパティ名が表示されたらそれをクリックします。

ss100811_15

これでmyNavigationControllerプロパティが、AppDelegate_iPhone.xibファイル内のナビゲーションコントローラとリンクします。上書き保存してからXCodeに戻ってください。

ちなみに、このXIBファイルにはWindowという部品もありますが、こちらはプロジェクト作成時点でAppDelegate_Sharedクラスのwindowプロパティとリンクされています。

■ファイルをコピーするメソッドを書く

iOS Application Programing Guide」によると、iPhoneアプリが起動するときは、まず「didFinishLaunchingWithOptions」メソッドが呼び出され、次に「applicationDidBecomeActive」メソッドが呼び出されるとのこと。iOS4でマルチタスク(高速スレッド切り替えと呼ぶべき?)に対応したので、起動後に他のアプリから切り替えたときは、前者は呼び出されず、後者だけが呼び出されるそうです。

(Programing Guideより引用)

リソースファイルをドキュメントフォルダにコピーする処理は起動時に一回だけやればいいので、applicationDidBecomeActiveメソッドに書きます。

★AppDelegate_iPhone.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
	// Override point for customization after application launch.
	 [window makeKeyAndVisible];

	//リソースのZIPファイルのパスを取得
	NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"samplebook.zip"];
	NSLog(@"resoruce=%@", filePath);

	//ドキュメントフォルダの場所を取得
	NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *docpath = [paths objectAtIndex:0];
	NSString *destPath = [docpath stringByAppendingPathComponent:@"samplebook.zip"];

	//リソースをドキュメントフォルダにコピー
	NSFileManager *fileman = [NSFileManager defaultManager];
	NSError *error;
	[fileman copyItemAtPath:filePath toPath:destPath error:&error]; 

	//ナビゲーションコントローラを追加
	[window addSubview:myNavigationController.view];
	return YES;
}

3行目までと21行目以降は最初からあるものなので、削ってしまうとちゃんと動作しなくなると思われます。

6行目で「samplebook.zip」という名前のリソースファイルのパスを取得し、NSString型変数filePathに記憶します。

10~11行目でアプリのドキュメントフォルダのパスを取得し、コピー先のファイルパスを作成します。

15行目でNSFileManagerのオブジェクトを取得し、copyItemAtPathメソッドでコピーします。

20行目はInterfaceBuilderで作成したナビゲーションコントローラを、メインウィンドウの上に配置する処理です。コントローラを直接置くことはできないので、その上のビューをviewプロパティで取り出して配置しています。

iOSのAPIについては、ネット上に日本語訳された情報が多数あるのですが、必ず公式のリファレンスライブラリも合わせて見ることをお奨めします。英語ですが「このメソッドはもう推奨しないので、××を使ったほうがいい」といった、最新の注釈が書かれているからです。

・iOS Reference Library(英語)

http://developer.apple.com/iphone/library/navigation/

・日本語翻訳された公式ドキュメント

https://developer.apple.com/jp/iphone/library/japanese.html

 

■アプリケーションを実行する

ツールバーのリストから「Simulator – 4.0 | Debug」を選び、「ビルドと実行」ボタンをクリックします。実行を中断したいときは隣の「タスク」ボタンをクリックします(どうして「タスク」という名前なのでしょうね?)。

ss100811_17

iPhoneシミュレータが起動して、アプリの画面が表示されたら成功です。

ss100811_19

FinderでiPhoneシミュレータの作業用フォルダを見ると、ちゃんとDocumentsフォルダにsamplebook.zipがコピーされているはずです。

作業用フォルダは、「ユーザー」→「ライブラリ」→「Application Support」→「iPhone Simulator」→「バージョン番号」→「Applications」→「アプリのGUID」にあります。

ss100811_20

今回のソースコードは以下からダウンロードできます。なお、「samplebook.zip」は市販マンガをスキャンしたものなので除いています。前々回の記事のリソースファイルに関する話を参考に、自分で追加してください。

ソースコードのダウンロード

 


余談ですが、実は最後の実行時点でトラブルが起きました。コードには問題がないはずなのに、アプリを起動してもナビゲーションバーが表示されません。

色々調べたところ、原因はInterfaceBuilderで「File’s OwnerとApp Delegateのリンクが切れていた」ことでした。これが切れるとAppDelegate_iPhoneクラスのメソッドが呼び出されません。

ss100811_18

プロジェクト作成時にリンク済みになっているはずなので、画面キャプチャを撮るときに誤ってクリックしてしまったのでしょう。

iPhoneアプリ開発では、初期状態で与えられるリンク設定やコードファイルの記述を不用意にいじると簡単に正常動作しなくなってしまいます。自分で作成したものでないだけに、原因を探すのは大変です。

ss100811_10.jpg

アプリが起動するところまで(2)

前回の記事の続きです。

■InterfaceBuilderでユーザーインターフェースを編集する

InterfaceBuilderを使って、iPhone版の画面を作っていきます。InterfaceBuilderというのは、VisualBasicのフォームデザイナー画面のようなものです。ただし、VisualBasicやC#に比べるとUIとコードの連携が密ではないので、結構そこで苦労します。正直、開発環境はあまり使いやすくない気がしますね……。ドツボにはまりやすいです。

とりあえず画面を作成しましょう。XCodeのプロジェクトツリーに「MainWindow_iPhone.xib」というファイルがあるので、それをダブルクリックすると、InterfaceBuilderが起動します。要するにXIBファイルがインターフェースのデータを保存したものなわけですね。

ちなみに、XIBファイルは昔NIB(NeXT Interface Builderの略)という名前だったので、その名残でAPI名や警告メッセージなどに「NIBファイル」という言葉が時々出てきます。その場合はXIBファイルを指定してやればいいです。

ss100811_10

InterfaceBuilderの画面は複数のウィンドウで構成されていて、左から「ライブラリ」「書類」「プレビュー」「インスペクター」となります。ライブラリは部品入れ、書類はプロジェクトツリーみたいなもの、プレビューは実際のUIのイメージ、インスペクターはプロパティシートみたいなものです。

ss100811_11

余談ですが、MacOSのアプリはうしろに他のアプリのウィンドウが見えるのが苦手です。結構間違えてクリックしちゃうんですよね。InterfaceBuilderは特に酷いというか、XIBファイルを複数開くともうわけがわかりません。

話を戻して、「書類」ウィンドウに注目してください。最初からいくつか項目があります。

ss100811_11

「File’s Owner」はアプリケーションそのものと関連づけられています。
「First Responder」は簡単に説明できないので省略します(申し訳ない)。
「App Delegate」はこのUIと関連づけられているクラス、つまりAppDelegate_iPhoneクラスを表しています。
「Window」はメインウィンドウです。

Typeの欄を見ると、それぞれが関連づけられているクラスがわかります。

 

■ナビゲーションコントローラを追加する

このアプリでは「ナビゲーションコントローラ」をメインのインターフェースにしています。ナビゲーションコントローラというのは、上のナビゲーションバーに表示されるボタンで階層移動できるインターフェースのことです。標準の「メール」「iPod」など、多くのアプリに使われています。

Libraryウィンドウから「Navigation Controler」を書類ウィンドウにドラッグ&ドロップします。

ss100811_12

「Navigation Controller」の上にはあらかじめ「Navigation Bar」や「View Controller」「Navigation Item」などの部品が配置されています。書類ウィンドウの「Navigation Controller」をダブルクリックするとプレビューウィンドウが開き、外観を確認できます。

ss100811_13

Libraryに登録されている部品には、「コントローラ」「ビュー」「その他のボタンなどのアイテム」の3種類があります。コントローラはビューを制御するためのもの、ビューは画面に表示されて実際に仕事をする部分です。

InterfaceBuilderでビューだけを配置しておいたら、後でコントローラにしかないメソッドが必要になった……なんてこともよくあるので注意が必要です。

■Navigation Itemの名前を変更する

Navigation Itemはナビゲーションバーに表示されるタイトルなどを記憶しておくための部品です。Navigation Controllerの子のView Controllerの子として配置されています。

最初に表示されている画面の名前を変更しましょう。書類ウィンドウでNavigation Itemをクリックするか、プレビューウィンドウのナビゲーションバーをクリックし、InspectorウィンドウのTitle欄に「ブック一覧」と入力します。

ss100811_21

これで外観はできあがったので、上書き保存してXCodeに切り替えます。

 

話は次の記事に続きます。

ss100811_01.jpg

アプリが起動するところまで(1)

さて、iPhone版マンガビューワですが、実は途中まで作ったものがあるのです。「ZIPファイルの一覧を表示」→「それを解凍して最初のページを表示」→「ピンチインで拡大縮小」&「ボタンクリックでページ送り」……というビューワとしての体裁をギリギリ保てるレベルまではできていました。

ところが、そこまで作ったところでいじる暇が無くなってしまい、そのまま1ヶ月以上経ってしまったため、細かいことはすっかり忘れてしまいました。

そこで復習も兼ねて最新SDKで作り直し、ついでに試行錯誤でグチャグチャになったクラス名などを整理し、さらにiPhone/iPad兼用のユニバーサルアプリにすることとします。

■プロジェクトの作成

まずはとりあえずユニバーサルアプリのプロジェクトを作成します。

ss100811_01

「ファイル」→「新規メニュー」を選び、「Product」のリストから「Universal」を選択します。他は「Window-based Application」を選び、「Use Core Data for storage」はオンにしています。

 

Continue reading “アプリが起動するところまで(1)” »