前回の記事の続きです。

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