iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
9月18日に、iOS上でゲームプログラミングにチャレンジしてみたい初級者を対象にセミナーを開催いたします。
【夜間/初級脱出】iOS開発セミナー:15パズル ゲームを作ろうiOSのCocoa Touchフレームワークには、横並びの画面はTab Bar Controller、階層方向はNavigaton Controllerが用意されているので、画面管理はこれを使えばいいのだが、標準のUIと異なる画面遷移が必要になった際に困ってしまう。
そうなったら、自分でビューの登録削除を行って独自の画面遷移を実装する事になるのだが、せっかくのビューコントローラが有効に活用できなくて残念と感じた事はないだろうか?
iOS 5から、Tab Bar Controllerの様なコンテナViewControllerを独自に実装できる機能がUIViewControllerに追加されたので、それを使って横並びの画面管理を実装してみた。
iOS 5から追加されたコンテナ機能の為のメソッドは、以下の4つ。
作成する画面は、灰色のTOPのビューコントローラの配下に赤と青の子ビューコントローラがあって、子っビューコントローラの画面上のボタンを押下すると、画面が切り替わるというものだ。
子ビューコントローラは、それぞれ、独立したStoryboardで作成した。
UIStoryboard *oneStoryboard = [UIStoryboard storyboardWithName:@"OneStoryboard" bundle:nil];
UIStoryboard *twoStoryboard = [UIStoryboard storyboardWithName:@"TwoStoryboard" bundle:nil];
OneViewController *oneViewController = [oneStoryboard instantiateInitialViewController];
TwoViewController *twoViewController = [twoStoryboard instantiateInitialViewController];
コンテナビューコントローラに子ビューコントローラを登録。
[self addChildViewController:oneViewController];
[self addChildViewController:twoViewController];
oneViewController.cvcViewController = self;
twoViewController.cvcViewController = self;
子ビュコントローラがコンテナビューコントローラ配下になった際の処理は、didMoveToParentViewController:に記述するのだが、コンテナビューコントローラの画面表示が完了した後でないと、addChildViewController:時に自動で呼ばれないようなので、呼ぶ必要がある場合は、自分で呼ぶ事になるようだ。
[oneViewController didMoveToParentViewController:self];
[twoViewController didMoveToParentViewController:self];
ビューコントローラの階層構造を作っても、Viewとは無関係の話。なので、最初の画面を表示するコードを記述しないといけない。
self.selectedViewController = [self.childViewControllers objectAtIndex:0];
[self.view addSubview:self.selectedViewController.view];
画面を切り替えるのは、メソッドtoggleVCで行っている。UIViewControllerには、画面切り替え用のメソッド– transitionFromViewController:toViewController:duration:options:animations:completion:を用意しているが、用意されている画面遷移のアニメーションでないのを望む場合は、自分でなんとかしないといけないようだ。それが、else分の青(two)から赤(one)に切り替わるコードとなる。
- (void)toggleVC
{
UIViewController *oneViewController = [self.childViewControllers objectAtIndex:0];
UIViewController *twoViewController = [self.childViewControllers objectAtIndex:1];
if (self.selectedViewController == oneViewController) {
[self transitionFromViewController:oneViewController
toViewController:twoViewController
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:NULL
completion:NULL];
self.selectedViewController = twoViewController;
}
else {
[UIView animateWithDuration:1.0
animations:^{
CGPoint fromPt = twoViewController.view.layer.position;
CGPoint toPt = CGPointMake(fromPt.x, (fromPt.y * -1.0));
twoViewController.view.layer.position = toPt;
}
completion:^(BOOL finished) {
/* 元の位置の戻す */
CGPoint fromPt = twoViewController.view.layer.position;
CGPoint toPt = CGPointMake(fromPt.x, (fromPt.y * -1.0));
twoViewController.view.layer.position = toPt;
/* 画面遷移(アニメーションなし) */
[self transitionFromViewController:twoViewController
toViewController:oneViewController
duration:0.0
options:0
animations:NULL
completion:NULL];
}];
self.selectedViewController = oneViewController;
}
}
StoryboardのトップのViewControlerはどうやって指定するのか?
InterfaceBuilderにチェック項目がありました!
第55回Cocoa勉強会(関東)の日程が決定いたしました。
日時: 2012/10/13(土) 13:00-17:00
会場:新宿三丁目 新宿伊藤ビル 4F
集合:現地
会費:500円
見学申込:http://www.cocoa-study.com/mail/
ナビゲーションコントローラやタブバーコントローラを使って画面遷移を実装していると、これがiOSで用意された画面遷移のAPIという印象を持ってしまうが、より高度なUIを実装する場合は、基本的な原理を理解していなと難しい為、画面遷移について基礎的なことから試行錯誤して理解を深めたいと考えた。
そこで、今回の内容がサブビューの追加と削除を使った画面遷移だ。
iOSにおける画面とはUIViewとそのサブクラスによって表示されるものだ。ビューはツルーのように配置でき、画面全体のビューを差し替える事が、画面遷移ということになる。
試してみよう。
Xcodeで雛形『Empty Application』の新規プロジェクトを生成する。
すると、AppDelegate.[hm]しかクラスのソースファイルが用意されていないプロジェクトが生成される。
そして、AppDelegate.mの内容を以下のようにカスタマイズする。
#import "AppDelegate.h"
@interface AppDelegate ()
@property (nonatomic, assign) BOOL isView1;
@property (nonatomic, strong) UIView *view1;
@property (nonatomic, strong) UIView *view2;
@end
@implementation AppDelegate
@synthesize isView1 = _isView1;
@synthesize view1 = _view1;
@synthesize view2 = _view2;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.view1 = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view1.backgroundColor = [UIColor redColor];
self.view2 = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view2.backgroundColor = [UIColor blueColor];
self.isView1 = YES;
[self.window addSubview:self.view1];
[self.window makeKeyAndVisible];
return YES;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.isView1) {
self.isView1 = NO;
[self.window addSubview:self.view2];
[self.view1 removeFromSuperview];
}
else {
self.isView1 = YES;
[self.window addSubview:self.view1];
[self.view2 removeFromSuperview];
}
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
self.view1 = nil;
self.view2 = nil;
}
@end
赤と青の画面が、タッチするとトグルに切り替わって表示されると思う。
最後にaddSubview:したビューが最前面となるため、そのビューが表示される。removeFromSuperviewで当該ビューは上位ビューから削除される。これを組み合わせると画面遷移となる。
ビューの追加と削除を行わなくても、画面を切り替える事ができる。画面全体サイズのビューを2つサブビューとして追加して、このサブビューの前後の階層を入れ替えるという方法だ。
アプリケーション起動時にview2もサブビューとして追加する。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.view1 = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view1.backgroundColor = [UIColor redColor];
self.view2 = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view2.backgroundColor = [UIColor blueColor];
[self.window addSubview:self.view2];
タッチされた時のコードを以下に変更する。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.isView1) {
self.isView1 = NO;
[self.window sendSubviewToBack:self.view1];
}
else {
self.isView1 = YES;
[self.window bringSubviewToFront:self.view1];
}
}
sendSubviewToBack:で指定したサブビューは背後に移動し、bringSubviewToFront:で前面に移動する。