Viewの切替

2010/07/06

iPhoneのViewの切り替えは、まだまだ日が浅いせいか色々頭を悩ませる部分。

UINavigationControllerのような階層的な切り替えでなく、多様なレイアウトが混在するような画面繊維、 例えばUITabBarControllerとUIViewControllerが独立して存在し、これらをある条件によって切替える場合。

例えば、iPhoneアプリ入門(Viewの変更)の方法のようにaddSubview:newViewやremoveFromSuperviewで 親viewに子のviewを追加、削除しています。

ただこのサンプルでは、viewが増えるごとにif文で条件分岐しなきゃいけない感じ(?)なので修正してみる。

InterfaceBuilderでTabBarApplicationをバインド

(1) UITabBarControllerを利用するので、プロジェクトはTabBarApplicationを選択する。 ※その他UINavigationController等を利用する場合は、手動でDelegate等の追加が必要

(2) 第一階層の画面は以下の2つを利用する

・トップ画面(ログイン前の画面群):(UIViewController) topViewController ・メイン画面(ログイン後の画面群):(TabBarController) mainViewController

topViewControllerは、ViewControllerで.h、.m、.xibを作成する。 ※サンプルではLoginViewControllerの名称で作成 mainViewControllerは、タブで表示する数だけViewControllerの.h、.m、.xibを作成する。

(3) アプリケーションデリゲート.hを修正する。 StockAppDelegate.h

@interface StockAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
    UIWindow *window;
    UITabBarController *mainViewController;
    UIViewController *topViewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UITabBarController *mainViewController;
@property (nonatomic, retain) IBOutlet UIViewController *topViewController;

-(void) showMainView;
-(void) showTopView;
-(void) showView:(UIView *)view;

(4) UIViewController、TabBarControllerを追加し、アウトレットを設定する。 iphone ※UIViewControllerは、NibにLoginViewControllerを指定している

(5) TabBarControllerに表示したいViewControllerを追加する。 iphone

(6) AppDelegateでUIViewController、TabBarControllerのdelgateを設定する。 iphone

View切替の実装

次にStockAppDelegate.mを修正

(1) mainViewController、topViewControllerを@synthesizeする。

@synthesize window;
@synthesize mainViewController;
@synthesize topViewController;

(2) showViewの実装

-(void) showView:(UIView *)view {
    for (UIView *subView in window.subviews) {
        [subView removeFromSuperview];
    }
    [window addSubview:view];
    [window makeKeyAndVisible];
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.75];
    [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.window cache:YES];
    [UIView commitAnimations]; 
}

windowに追加されたsubviewsがあれば全て削除し、指定したviewを追加する。 adddSubviewの他に、insertSubview、bringSubview、sendSubview、eschangeSubviewAtIndexがあるので、 削除する方式ではなく、これらを利用しても良いかも知れない。 ※参考:福井高専IT研究所 UIView

切り替えアニメーションは、viewではなくアプリケーションのメインビュー(window)で実現している。

(3) showTopView、showMainViewの実装

-(void) showTopView {
  [self showView:topViewController.view];  
}
-(void) showMainView {
  [self showView:mainViewController.view];
}

showViewで、表示したいviewを引数として実行している。

(4) didFinishLaunchingWithOptionsの実装

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    [self showTopView];
    return YES;
}

アプリケーション起動時に、topViewController.viewを表示するようにする。

----2010/09/01追記 didFinishLaunchingWithOptionsに記述するのはあまりよろしくない処理と思います。 View遷移を管理するクラスを作った方がよさげです。

(5) LoginViewControllerで、ビュー切替えの処理を実装する。 LoginViewController.xib iphone ※ログインボタンをクリックしたらmainViewController.viewを表示する。

LoginViewController.m

-(IBAction) changeView:(id)sender {
    if (TRUE) {
        StockAppDelegate *delegate = (StockAppDelegate*)[[UIApplication sharedApplication] delegate];
        [delegate showMainView];
    }
}

ただ、これだとDelegateの意味がないか・・・。 普通に、UIApplication showMainViewでいいもんね。 そもそもUIApplicationにメソッド書いてる時点で駄目っすね。

----2010/09/01追記 LoginViewControllerにdelegateを定義し、showMainView:の定義されているクラスでLoginViewControllerにdelegateを受け渡してやるのが正しいか?

View切替をするカスタムクラス

- (void)viewDidLoad {
    loginViewController.delegate = self;
}

- (void)showMainView {
    ....
}
@protocol LoginViewControllerDelegate;

@interface LoginViewController : UIViewController {
    id  delegate;
}

@property (nonatomic, assign) id <LoginViewControllerDelegate> delegate;

@end

@protocol LoginViewControllerDelegate
-(void)showMainView;
@end

これで、LoginViewController上でdelegateを経由してshowMainView:を実行できる。

delegate云々もそうですが、UIViewController、UITabNavigationController、UITabBarControllerが複雑に入り組んだアプリを作成する場合、どんな画面設計にするか?の方が重要ですね(^_^;)