パズルのめどが立ったんで、パズルメーカーを本格的にデザインします。
まあ、だいたいこんな感じの要求を満たさんといかんわけですな。
なので、タブを使って貰い物、自分の物別にサムネイル表示できるようにしようと思います。
ま、軽くiPadにも対応しようかなと。スプリットビューとか凝ったもんじゃなく、単に大きな画面(2倍表示じゃなく)でできるようにするだけだけどね。
時代はユニバーサル
とにかく久々に新規プロジェクトを作成。
XcodeのFile>New>New Project…メニュー選択だ!
出てきたテンプレート選択画面ではiOSのApplicationタブを選んで、Window based-Applicationテンプレートを選択。
え、Tab bar Applicationテンプレートの方じゃないんすか?と思った人。
いい質問です。
現状のSDKはWindow based-ApplicationテンプレートしかDevice FamilyのUniversalが選べねーんすよ。
こいつね
で作りました。Company Identifierは適当に自分のつけたいようにつけてください。
ま、Tab bar Applicationテンプレートでプロジェクト作っておいて、あとからDevice FamilyをUniversalにしても全然問題ないんですけどね。
今回、サムネイル表示にはUITableViewを使わず、写真アプリみたいに画像をタイル上に並べたいと思っているんで、わざわざTab bar Applicationテンプレートで用意されたUITableViewControllerを取り外したりとかも面倒なんですよ。
なのでUITabBarControllerもUINavigationControllerもUIViewControllerも直に作ります。
ちなにみiOS 5 SDK版のXcodeに用意されたテンプレートにはあんなものや、こんなものまで~。はやく無料会員にも公開されるといいですな。
プロジェクトが作られたらMainWindow_iPhone.xibをクリックして、Interface builder使ってwindowに貼られてるメッセージを削除しましょう。
クリックして選択状態にしてDeleteキーで削除できます。
こいつはMainWindow_iPad.xib側にもあるんで、そっちも削除。
これが終わったらpuzzlerAppDelegate.mの-application:didFinishLaunchingWithOptions:メソッドで
を作ります。
タブ切り替えのアプリケーションてのは、UITabBarControllerインスタンスに、管理して欲しいUIViewControllerインスタンスの配列を渡す事から始まるんですな。
UINavigationControllerインスタンスもUIViewControllerクラスを継承してるので当然渡す事ができます。UINavigationControllerインスタンスを使う理由はサムネイルクリックでパズルゲーム画面に遷移するのにpushViewControllerを使い、戻るのにpopViewControllerを使おうと思うから。
presentModalViewController使うならUINavigationControllerインスタンスを作らず直接UIViewControllerインスタンス渡してもいいです。
ここらへんの会話がチンプンカンプンな人はドリルを読みましょう。
UIViewController関連のドリル
まず、左側:貰い物画面用のUINavigationControllerインスタンス、UIViewControllerインスタンスはこんな感じで作成。
仮組なので、サムネイル表示のカスタムUIViewControllerクラスは用意せず、UIViewControllerインスタンスそのまんまをUINavigationControllerのinitWithRootViewControllerに渡してます。
右側:自分の物用のUINavigationControllerインスタンス、UIViewControllerインスタンスも同じ感じで作成し、この2つのUINavigationControllerインスタンスをNSArrayに入れてUITabBarControllerインスタンスのviewControllersプロパティに設定します。
あとはwindowにUITabBarControllerのviewを貼付けておしまい。
-application:didFinishLaunchingWithOptions:メソッド全体はこんな感じ。
これでビルドして実行するとちゃんとタブ切り替えできる赤、青画面がでるわけですわ。
タブにアイコンがないの寂しーなと思う人は、とりあえずシステム付属のタブバーアイテムを指定しちゃってください。
・
・
recievedNavController.tabBarItem = [[[UITabBarItem alloc]
initWithTabBarSystemItem:UITabBarSystemItemFavorites tag:1] autorelease];
privateNavController.tabBarItem = [[[UITabBarItem alloc]
initWithTabBarSystemItem:UITabBarSystemItemHistory tag:2] autorelease];
UITabBarController* tabBarController = [[UITabBarController alloc] init];
・
・
これで画面上は
こんな風になります。
ただ、こいつだとタイトルもシステム標準になっちゃうんだよね。最終的には自前でアイコン用意しなと駄目だね。
UITabBarControllerインスタンスではなくUINavigationControllerインスタンスのtabBarItemに設定してるのがみそです。
タブバー画面自体はUITabBarControllerインスタンスが持つtabBarなんですが、各タブの部分は、UITabBarControllerインスタンスが管理してるUIViewControllerインスタンスのtabBarItemプロパティに基づいて用意されるんですな。
さっきも書いたようにUINavigationControllerはUIViewControllerを継承してるので、当然tabBarItemプロパティ持ってます。
ちなみに、UINavigationControllerのtabBarItemプロパティがnilだった場合、UINavigationControllerのルートUIViewControllerのtabBarItemプロパティが利用されます。recievedNavControllerのtabBarItemがnilならreceivedThumbControllerのtabBarItemが利用されるわけですな。
で、そいつもnilなら、まずrecievedNavControllerのtitleプロパティ、それがnilならreceivedThumbControllerのtitleプロパティを調べて文字だけで表示しようとします。
これが、
としてるとタブに「貰い物」て文字が出た理由です。
ちなみに全部nilならのっぺらぼうになります。
あと、UINavigationControllerのナビゲーションバーも同じような感じです。
ナビゲーションバー画面自体はUINavigationControllerインスタンスが持つnavigationBarなんですが、表示される内容は、その時に表示されているUIViewControllerインスタンスのnavigationItemプロパティに基づいて用意されるんですな。
で、このナビゲーションバーのタイトルもUIViewControllerインスタンスのnavigationItemプロパティがnilならtitleプロパティ側が利用されるぞっと。
なんかUINavigationControllerの管理するUIViewControllerインスタンスとしてUITabBarControllerインスタンス使うと、とんでもなくカオスじゃないすか?
と思う人もいると思うけど、これはAppleが明確に禁止しています。
UITabBarControllerの管理対象にUINavigationControllerを渡してもいいけど、逆は駄目だそうです。
前にも書いたか…
まあいい、次回はサムネイルビュー画面の作成。
------------
サンプルプロジェクト:puzzler.zip
カメラやアルバムから取込んだ画像をパズルにできる。
パズルは複数持てサムネイルで一覧できる。
進行途中で別のパズルに切り替えることが出来る。
メールでもらったパズルと、自分が作ったパズルを分別できる。
パズルは複数持てサムネイルで一覧できる。
進行途中で別のパズルに切り替えることが出来る。
メールでもらったパズルと、自分が作ったパズルを分別できる。
まあ、だいたいこんな感じの要求を満たさんといかんわけですな。
なので、タブを使って貰い物、自分の物別にサムネイル表示できるようにしようと思います。
ま、軽くiPadにも対応しようかなと。スプリットビューとか凝ったもんじゃなく、単に大きな画面(2倍表示じゃなく)でできるようにするだけだけどね。
時代はユニバーサル
とにかく久々に新規プロジェクトを作成。
XcodeのFile>New>New Project…メニュー選択だ!
出てきたテンプレート選択画面ではiOSのApplicationタブを選んで、Window based-Applicationテンプレートを選択。
え、Tab bar Applicationテンプレートの方じゃないんすか?と思った人。
いい質問です。
現状のSDKはWindow based-ApplicationテンプレートしかDevice FamilyのUniversalが選べねーんすよ。
こいつね
Product Name:puzzler
Company Identifier:jp.mycompany
Device Family:Universal
Use Core Data:チェックせず
Include Unit Tests:チェックせず
Company Identifier:jp.mycompany
Device Family:Universal
Use Core Data:チェックせず
Include Unit Tests:チェックせず
で作りました。Company Identifierは適当に自分のつけたいようにつけてください。
ま、Tab bar Applicationテンプレートでプロジェクト作っておいて、あとからDevice FamilyをUniversalにしても全然問題ないんですけどね。
今回、サムネイル表示にはUITableViewを使わず、写真アプリみたいに画像をタイル上に並べたいと思っているんで、わざわざTab bar Applicationテンプレートで用意されたUITableViewControllerを取り外したりとかも面倒なんですよ。
なのでUITabBarControllerもUINavigationControllerもUIViewControllerも直に作ります。
ちなにみiOS 5 SDK版のXcodeに用意されたテンプレートにはあんなものや、こんなものまで~。はやく無料会員にも公開されるといいですな。
プロジェクトが作られたらMainWindow_iPhone.xibをクリックして、Interface builder使ってwindowに貼られてるメッセージを削除しましょう。
クリックして選択状態にしてDeleteキーで削除できます。
こいつはMainWindow_iPad.xib側にもあるんで、そっちも削除。
これが終わったらpuzzlerAppDelegate.mの-application:didFinishLaunchingWithOptions:メソッドで
UITabBarControllerインスタンス:1つ
UINavigationControllerインスタンスとUIViewControllerインスタンスのペア:2つ
UINavigationControllerインスタンスとUIViewControllerインスタンスのペア:2つ
を作ります。
タブ切り替えのアプリケーションてのは、UITabBarControllerインスタンスに、管理して欲しいUIViewControllerインスタンスの配列を渡す事から始まるんですな。
UINavigationControllerインスタンスもUIViewControllerクラスを継承してるので当然渡す事ができます。UINavigationControllerインスタンスを使う理由はサムネイルクリックでパズルゲーム画面に遷移するのにpushViewControllerを使い、戻るのにpopViewControllerを使おうと思うから。
presentModalViewController使うならUINavigationControllerインスタンスを作らず直接UIViewControllerインスタンス渡してもいいです。
ここらへんの会話がチンプンカンプンな人はドリルを読みましょう。
UIViewController関連のドリル
まず、左側:貰い物画面用のUINavigationControllerインスタンス、UIViewControllerインスタンスはこんな感じで作成。
// 貰い物側サムネイル画面担当予定、タイトルは貰い物、背景は赤
UIViewController* receivedThumbController = [[[UIViewController alloc]init] autorelease];
receivedThumbController.title = @"貰い物";
receivedThumbController.view.backgroundColor = [UIColor redColor];
// 貰い物側ナビゲーションコントローラ、バーは半透明
UINavigationController* recievedNavController = [[[UINavigationController alloc]
initWithRootViewController:receivedThumbController]autorelease];
recievedNavController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
UIViewController* receivedThumbController = [[[UIViewController alloc]init] autorelease];
receivedThumbController.title = @"貰い物";
receivedThumbController.view.backgroundColor = [UIColor redColor];
// 貰い物側ナビゲーションコントローラ、バーは半透明
UINavigationController* recievedNavController = [[[UINavigationController alloc]
initWithRootViewController:receivedThumbController]autorelease];
recievedNavController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
仮組なので、サムネイル表示のカスタムUIViewControllerクラスは用意せず、UIViewControllerインスタンスそのまんまをUINavigationControllerのinitWithRootViewControllerに渡してます。
右側:自分の物用のUINavigationControllerインスタンス、UIViewControllerインスタンスも同じ感じで作成し、この2つのUINavigationControllerインスタンスをNSArrayに入れてUITabBarControllerインスタンスのviewControllersプロパティに設定します。
// タブコントローラ作って登録
UITabBarController* tabBarController = [[UITabBarController alloc] init];
NSArray* viewcontrollers = [NSArray arrayWithObjects:recievedNavController,
privateNavController, nil];
tabBarController.viewControllers = viewcontrollers;
UITabBarController* tabBarController = [[UITabBarController alloc] init];
NSArray* viewcontrollers = [NSArray arrayWithObjects:recievedNavController,
privateNavController, nil];
tabBarController.viewControllers = viewcontrollers;
あとはwindowにUITabBarControllerのviewを貼付けておしまい。
[self.window addSubview:tabBarController.view];
-application:didFinishLaunchingWithOptions:メソッド全体はこんな感じ。
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 貰い物側サムネイル画面担当予定、タイトルは貰い物、背景は赤
UIViewController* receivedThumbController = [[[UIViewController alloc]init] autorelease];
receivedThumbController.title = @"貰い物";
receivedThumbController.view.backgroundColor = [UIColor redColor];
// 貰い物側ナビゲーションコントローラ、バーは半透明
UINavigationController* recievedNavController = [[[UINavigationController alloc]
initWithRootViewController:receivedThumbController]autorelease];
recievedNavController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
// 個人所有側サムネイル画面担当予定、タイトルは自分のもの、背景は青
UIViewController* privateThumbController = [[[UIViewController alloc]init] autorelease];
privateThumbController.title = @"自分のもの";
privateThumbController.view.backgroundColor = [UIColor blueColor];
// 貰い物側ナビゲーションコントローラ、バーは半透明
UINavigationController* privateNavController = [[[UINavigationController alloc]
initWithRootViewController:privateThumbController]autorelease];
privateNavController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
// タブコントローラ作って登録
UITabBarController* tabBarController = [[UITabBarController alloc] init];
NSArray* viewcontrollers = [NSArray arrayWithObjects:
recievedNavController, privateNavController, nil];
tabBarController.viewControllers = viewcontrollers;
[self.window addSubview:tabBarController.view];
[self.window makeKeyAndVisible];
return YES;
}
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 貰い物側サムネイル画面担当予定、タイトルは貰い物、背景は赤
UIViewController* receivedThumbController = [[[UIViewController alloc]init] autorelease];
receivedThumbController.title = @"貰い物";
receivedThumbController.view.backgroundColor = [UIColor redColor];
// 貰い物側ナビゲーションコントローラ、バーは半透明
UINavigationController* recievedNavController = [[[UINavigationController alloc]
initWithRootViewController:receivedThumbController]autorelease];
recievedNavController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
// 個人所有側サムネイル画面担当予定、タイトルは自分のもの、背景は青
UIViewController* privateThumbController = [[[UIViewController alloc]init] autorelease];
privateThumbController.title = @"自分のもの";
privateThumbController.view.backgroundColor = [UIColor blueColor];
// 貰い物側ナビゲーションコントローラ、バーは半透明
UINavigationController* privateNavController = [[[UINavigationController alloc]
initWithRootViewController:privateThumbController]autorelease];
privateNavController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
// タブコントローラ作って登録
UITabBarController* tabBarController = [[UITabBarController alloc] init];
NSArray* viewcontrollers = [NSArray arrayWithObjects:
recievedNavController, privateNavController, nil];
tabBarController.viewControllers = viewcontrollers;
[self.window addSubview:tabBarController.view];
[self.window makeKeyAndVisible];
return YES;
}
これでビルドして実行するとちゃんとタブ切り替えできる赤、青画面がでるわけですわ。
タブにアイコンがないの寂しーなと思う人は、とりあえずシステム付属のタブバーアイテムを指定しちゃってください。
・
・
recievedNavController.tabBarItem = [[[UITabBarItem alloc]
initWithTabBarSystemItem:UITabBarSystemItemFavorites tag:1] autorelease];
privateNavController.tabBarItem = [[[UITabBarItem alloc]
initWithTabBarSystemItem:UITabBarSystemItemHistory tag:2] autorelease];
UITabBarController* tabBarController = [[UITabBarController alloc] init];
・
・
これで画面上は
こんな風になります。
ただ、こいつだとタイトルもシステム標準になっちゃうんだよね。最終的には自前でアイコン用意しなと駄目だね。
UITabBarControllerインスタンスではなくUINavigationControllerインスタンスのtabBarItemに設定してるのがみそです。
タブバー画面自体はUITabBarControllerインスタンスが持つtabBarなんですが、各タブの部分は、UITabBarControllerインスタンスが管理してるUIViewControllerインスタンスのtabBarItemプロパティに基づいて用意されるんですな。
さっきも書いたようにUINavigationControllerはUIViewControllerを継承してるので、当然tabBarItemプロパティ持ってます。
ちなみに、UINavigationControllerのtabBarItemプロパティがnilだった場合、UINavigationControllerのルートUIViewControllerのtabBarItemプロパティが利用されます。recievedNavControllerのtabBarItemがnilならreceivedThumbControllerのtabBarItemが利用されるわけですな。
で、そいつもnilなら、まずrecievedNavControllerのtitleプロパティ、それがnilならreceivedThumbControllerのtitleプロパティを調べて文字だけで表示しようとします。
これが、
receivedThumbController.title = @"貰い物";
としてるとタブに「貰い物」て文字が出た理由です。
ちなみに全部nilならのっぺらぼうになります。
あと、UINavigationControllerのナビゲーションバーも同じような感じです。
ナビゲーションバー画面自体はUINavigationControllerインスタンスが持つnavigationBarなんですが、表示される内容は、その時に表示されているUIViewControllerインスタンスのnavigationItemプロパティに基づいて用意されるんですな。
で、このナビゲーションバーのタイトルもUIViewControllerインスタンスのnavigationItemプロパティがnilならtitleプロパティ側が利用されるぞっと。
なんかUINavigationControllerの管理するUIViewControllerインスタンスとしてUITabBarControllerインスタンス使うと、とんでもなくカオスじゃないすか?
と思う人もいると思うけど、これはAppleが明確に禁止しています。
UITabBarControllerの管理対象にUINavigationControllerを渡してもいいけど、逆は駄目だそうです。
前にも書いたか…
まあいい、次回はサムネイルビュー画面の作成。
------------
サンプルプロジェクト:puzzler.zip