Cocoa練習帳 -20ページ目

第55回Cocoa勉強会(関東)

今回は、ActionScriptでiOSアプリケーションを開発するお話と、UIViewControllerのコンテナ機能、EventKitとリマインダー、OSXのRetina Displya対応の発表がありました。ちょっと試してみたい内容があって興味深かったです。

[iOS]画面遷移(コンテナViewController)

以前のサンプルと比較しやすくするため、ビューを使った画面遷移のサンプルコードをビューコントローラのコンテナ機能を使った物に変更してみた。




TransitionViewのAppDelegate.mを以下の内容に変更。




- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    CGRect  screenBounds = [[UIScreen mainScreen] applicationFrame];
    CGRect  windowBounds = [[UIScreen mainScreen] bounds];
     
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    
    self.containerViewController = [[UIViewController alloc] init];
    self.containerViewController.view.backgroundColor = [UIColor yellowColor];
    self.myViewController1 = [[MyViewController alloc] init];
    self.myViewController1.view.backgroundColor = [UIColor redColor];
    self.myViewController1.title = @"one";
    self.myViewController2 = [[MyViewController alloc] init];
    self.myViewController2.view.backgroundColor = [UIColor blueColor];
    self.myViewController2.title = @"two";
    
    self.window.rootViewController = self.containerViewController;
    
    /* コンテナViewControllerの子ViewControllerに登録 */
    [self.containerViewController addChildViewController:self.myViewController1];
    [self.containerViewController addChildViewController:self.myViewController2];
    
    /* 強制的に呼ぶ */
    [self.myViewController1 didMoveToParentViewController:self.containerViewController];
    [self.myViewController2 didMoveToParentViewController:self.containerViewController];
    
    /* 最初の画面を設定 */
    self.isView1 = YES;
    CGRect  frame = self.myViewController1.view.frame;
    frame.origin = CGPointMake(0.0, 0.0);
    self.myViewController1.view.frame = frame;
    self.myViewController2.view.frame = frame;
    [self.containerViewController.view addSubview:self.self.myViewController1.view];
    
    [self.window makeKeyAndVisible];
    return YES;
}
 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (self.isView1) {
        self.isView1 = NO;
        [self.containerViewController transitionFromViewController:self.myViewController1
                                                  toViewController:self.myViewController2
                                                          duration:1.0
                                                           options:UIViewAnimationOptionTransitionCrossDissolve
                                                        animations:NULL
                                                        completion:NULL];
    }
    else {
        self.isView1 = YES;
        [self.containerViewController transitionFromViewController:self.myViewController2
                                                  toViewController:self.myViewController1
                                                          duration:1.0
                                                           options:UIViewAnimationOptionTransitionCrossDissolve
                                                        animations:NULL
                                                        completion:NULL];
    }
}



ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/TransitionView - GitHub

https://github.com/murakami/workbook/tree/master/ios/ContainerVC - GitHub



関連情報
iOS View プログラミングガイド



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[iOS]画面遷移(始点)

「独自のコンテナViewController」に戻る。




サンプルコードはRetina 4inchは考慮していないので、試す場合は、以前の画面サイズで行って欲しい。画面と同じサイズのDefault.pngを用意する。その際、座標が分かりやすい図とする。そして、最初に表示するViewControllerの画面にも、Default.pngを貼付けて欲しい。そして、実行するとどうなるか?最初の画面が表示される際に、図が少し下がったように見えないか?




これは、親ビューコントローラのビューの座標の始点が、ステータスバー分だけ下がっているが、nibでの子ビューコントローラーのビューの座標が、ステータスバーを考慮して下げられているからだ。




なので、小ビューコントローラのビューの始点を原点にすれば、解決する。




@implementation CVCViewController
...
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    /* 子ビューコントローラを取得 */
    UIStoryboard    *oneStoryboard = [UIStoryboard storyboardWithName:@"OneStoryboard" bundle:nil];
    UIStoryboard    *twoStoryboard = [UIStoryboard storyboardWithName:@"TwoStoryboard" bundle:nil];
    OneViewController   *oneViewController = [oneStoryboard instantiateInitialViewController];
    TwoViewController   *twoViewController = [twoStoryboard instantiateInitialViewController];
    
    /* コンテナViewControllerの子ViewControllerに登録 */
    [self addChildViewController:oneViewController];
    [self addChildViewController:twoViewController];
    oneViewController.cvcViewController = self;
    twoViewController.cvcViewController = self;
    
    /* 強制的に呼ぶ */
    [oneViewController didMoveToParentViewController:self];
    [twoViewController didMoveToParentViewController:self];
    
    /* 最初の画面を設定 */
    self.selectedViewController = [self.childViewControllers objectAtIndex:0];
    CGRect  frame = self.selectedViewController.view.frame;
    frame.origin = CGPointMake(0.0, 0.0);
    self.selectedViewController.view.frame = frame;
    [self.view addSubview:self.selectedViewController.view];
}
...
@end



これで、少し悩んだ事があった。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/TransitionView - GitHub

https://github.com/murakami/workbook/tree/master/ios/ContainerVC - GitHub



関連情報
iOS View プログラミングガイド



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[iOS]画面遷移(ビューコントローラについて)

画面遷移とは、ビューの入れ替えと考えるれば、ビューさえ扱えば画面遷移が実装できるという事になるが、ビューコントローラには画面単位でのビューの生成/削除や、画面が開店した際に対応したイベントを受け取れるので、ビューコントローラを利用しない手はないということになるが、イベントのフレームワーク内部で管理され、フレームワークで用意されているナビゲーションコントローラやタブバーコントローラを使えば、その恩恵に預かる事は出来るが、これらを使わず、かつ、nibを利用しない場合は、その恩恵に預かる事は難しい。




これに対しての解決策として、iOS5からビューコントローラに子ビューコントローラを管理するコンテナ機能を追加されたのだと思う。これの利用の方法については、以前、紹介したが、実際に使ってみて気がついて細かな点について、発表していきたいと思う。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/TransitionView - GitHub

https://github.com/murakami/workbook/tree/master/ios/ContainerVC - GitHub



関連情報
iOS View プログラミングガイド



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[iOS]画面遷移(ルートビューコントローラの切り替え)

では、ルートビューコントローラを使って画面遷移はできないか?試してみよう。




ビューの時と同様に赤と青の二つのビューコントローラを生成し、初めは赤を表示する。




- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    CGRect  screenBounds = [[UIScreen mainScreen] applicationFrame];
    CGRect  windowBounds = [[UIScreen mainScreen] bounds];
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    
    self.myViewController1 = [[MyViewController alloc] init];
    self.myViewController1.view.backgroundColor = [UIColor redColor];
    self.myViewController2 = [[MyViewController alloc] init];
    self.myViewController2.view.backgroundColor = [UIColor blueColor];
    
    self.isView1 = YES;
    self.window.rootViewController = self.myViewController1;
    
    [self.window makeKeyAndVisible];
    return YES;
}



そして、これもビューとのとき同様にタッチされたら切り替える。




- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (self.isView1) {
        self.isView1 = NO;
        self.window.rootViewController = self.myViewController2;
    }
    else {
        self.isView1 = YES;
        self.window.rootViewController = self.myViewController1;
    }
}



画面は切り替わった。そして、viewDidLoad等が呼ばれるとログを出力するようにしたところ、alloc/initしたタイミングでviewDidLoadまでは呼ばれている。そして、ルートビューコントローラに設定したらviewWillAppear:等が呼ばれる。viewWillAppear:等が呼ばれるのは、タッチの際のルートビューコントローラの変更時でも同様だ。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/TransitionView - GitHub


関連情報
iOS View プログラミングガイド



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[iOS]画面遷移(ルートビューコントローラ)

Apple Developerサイトの文書はよく読むべきだ。




iOS4からウィンドウにrootViewControllerというプロパティが追加された。これに生成したビューコントローラを設定してみよう。




- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ....    
    self.myViewController = [[MyViewController alloc] init];
    self.myViewController.view.backgroundColor = [UIColor yellowColor];
    self.window.rootViewController = self.myViewController;
    ....
    [self.window makeKeyAndVisible];
    return YES;
}



viewDidLoad等が呼ばれるようになった。これで、最初のビューコントローラの管理する問題は解決した。これ以降については、次の機会に。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/TransitionView - GitHub


関連情報
iOS View プログラミングガイド



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[iOS]画面遷移(ビューコントローラについて)

何事も試してみないと分からない。今回は少しおかしいと思う事もあえてやってみる。




ビューを管理するものといてビューコントローラがあるが、それをInterface Builderを使わないで作成してみよう。




新規ファイルとして、UIViewControllerのサブクラスを作成。ビューコントローラの実装が必要なメソッドにデバッグ出力を埋め込んでみた。




#import <UIKit/UIKit.h>
 
@interface MyViewController : UIViewController
@end



#import "MyViewController.h"
 
@interface MyViewController ()
- (void)_init;
@end
 
@implementation MyViewController
 
- (id)init
{
    DBGMSG(@"%s", __func__);
    self = [super init];
    if (self) {
        [self _init];
    }
    return self;
}
 
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    DBGMSG(@"%s", __func__);
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        [self _init];
    }
    return self;
}
 
- (id)initWithCoder:(NSCoder *)aDecoder
{
    DBGMSG(@"%s", __func__);
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self _init];
    }
    return self;
}
 
- (void)_init
{
    DBGMSG(@"%s", __func__);
}
 
- (void)dealloc
{
    DBGMSG(@"%s", __func__);
}
 
- (void)loadView
{
    DBGMSG(@"%s", __func__);
    [super loadView];
}
 
- (void)viewDidLoad
{
    DBGMSG(@"%s", __func__);
    [super viewDidLoad];

}
 
- (void)viewDidUnload
{
    DBGMSG(@"%s", __func__);
    [super viewDidUnload];
}
 
- (void)viewWillAppear:(BOOL)animated
{
    DBGMSG(@"%s", __func__);
    [super viewWillAppear:animated];
}
 
- (void)viewDidAppear:(BOOL)animated
{
    DBGMSG(@"%s", __func__);
    [super viewDidAppear:animated];
}
 
- (void)viewWillDisappear:(BOOL)animated
{
    DBGMSG(@"%s", __func__);
    [super viewWillDisappear:animated];
}
 
- (void)viewDidDisappear:(BOOL)animated
{
    DBGMSG(@"%s", __func__);
    [super viewDidDisappear:animated];
}
 
- (void)didReceiveMemoryWarning
{
    DBGMSG(@"%s", __func__);
    [super didReceiveMemoryWarning];
}
 
@end



そして、このビューコントローラのインスタンスを生成して、ビューを取り出してみる。




- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ....    
    self.myViewController = [[MyViewController alloc] init];
    ....
}



当たり前だが、viewDidLoad等は呼ばれない。つまり、ビューコントローラはInterface Builderで生成されるものということか?




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/TransitionView - GitHub


関連情報
iOS View プログラミングガイド



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[iOS]画面遷移(画面サイズ)

先日のビューによる画面遷移では、二通りに画面サイズを取得するコードがあったと思うが、その値を見てみる事にする。




CGRect  screenBounds = [[UIScreen mainScreen] applicationFrame];
DBGMSG(@"screenBounds:(%f, %f, %f, %f)",
    screenBounds.origin.x,
    screenBounds.origin.y,
    screenBounds.size.width,
    screenBounds.size.height);
CGRect  windowBounds = [[UIScreen mainScreen] bounds];
DBGMSG(@"windowBounds:(%f, %f, %f, %f)",
    windowBounds.origin.x,
    windowBounds.origin.y,
    windowBounds.size.width,
    windowBounds.size.height);



後の話で出てくるが、注目すべきは、screenBoundsはステータスバー分、Y軸方向に20下がっている。この事が、nibを使った場合に、気をつけないと行けない事になるが、詳しくは次の機会で。




補足の説明だ。




frameは上位ビューの座標系の値となっている。なので、Y軸の値はステータスバー分、下がっている。boundsはローカル座標系の値なので、上位との位置関係を考慮しないので、原点(0, 0)からの値となっている。




windowのサイズは画面一杯にする。なので、ステータス分は考えない、画面のサイズを設定する。ビューは、表示される領域のサイズにしないと、その部分は隠れてしまう。サンプルでも、そのようにしている。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/TransitionView - GitHub


関連情報
iOS View プログラミングガイド



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[iOS]画面遷移(サブビューの追加と削除)

ナビゲーションコントローラやタブバーコントローラを使って画面遷移を実装していると、これが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];


    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 sendSubviewToBack:self.view1];
    }
    else {
        self.isView1 = YES;
        [self.window bringSubviewToFront:self.view1];
    }
}



sendSubviewToBack:で指定したサブビューは背後に移動し、bringSubviewToFront:で前面に移動する。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/TransitionView - GitHub


関連情報
iOS View プログラミングガイド



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[Cocoa]第55回Cocoa勉強会(関東)

第55回Cocoa勉強会(関東)の日程が決定いたしました。




日時: 2012/10/13(土) 13:00-17:00
会場:新宿三丁目 新宿伊藤ビル 4F
集合:現地
会費:500円
見学申込:http://www.cocoa-study.com/mail/



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)