DelegateによるView切替え

2010/07/07

やっとDelegateについてわかってきた。 自分のクラスを他のクラスに受け渡す際、新たにクラスのインスタンスを作成しなくてくてもできる!ってことかも。 以下のように、複数のViewControllerを管理用のViewControllerを経由して切り替えてみる。

YosouViewController(管理用) LoginViewController(表示用) TopViewController(表示用) ・LoginViewControllerでログインボタンがクリックされたら、TopViewControllerに切り替える。 ・ページ切替の処理は、子のViewControllerからYosouViewControllerDelegateを経由してYosouViewControllerに委任する。

YosouViewController.h

@protocol YosouViewControllerDelegate;

@class LoginViewController;
@class TopViewController;

@interface YosouViewController : UIViewController {
    id <YosouViewControllerDelegate> delegate;
    LoginViewController *loginViewController;
    TopViewController *topViewController;
}

@property (nonatomic, assign) id <YosouViewControllerDelegate> delegate;
@property (nonatomic, retain) IBOutlet LoginViewController *loginViewController;
@property (nonatomic, retain) IBOutlet TopViewController *topViewController;

-(void) page:(UIView *)view;

@end

@protocol YosouViewControllerDelegate
@optional
-(void) pageLogin;
-(void) pageTop;
@end

まず参照されるクラスでDelegateを定義する。

Delegateメソッドの定義で、@optionalにしておかないとDelegateを設定したクラスで警告がでる

YosouViewController.m

#import "YosouViewController.h"
#import "LoginViewController.h"
#import "TopViewController.h"

@implementation YosouViewController

@synthesize delegate;
@synthesize loginViewController;
@synthesize topViewController;

...

- (void)viewDidLoad {
    delegate = (id)self;
    
    [delegate pageLogin];
    [super viewDidLoad];
}

...

#pragma mark -
#pragma mark Custom Methods
-(void) page:(UIView *)view {
    NSLog(@"page");
    for (UIView *subView in self.view.subviews) {
        [subView removeFromSuperview];
    }
    [self.view addSubview:view];
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.75];
    [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:YES];
    [UIView commitAnimations]; 
}

#pragma mark -
#pragma mark Delegate Methods
-(void) pageLogin {
    NSLog(@"pageLogin");
    if (loginViewController == nil) {
        loginViewController = [[LoginViewController alloc]
                           initWithNibName:@"LoginViewController"
                           bundle:nil];
        loginViewController.delegate = self;
    }
    [self page:loginViewController.view];
}
-(void) pageTop {
    NSLog(@"pageTop");
    if (topViewController == nil) {
        topViewController = [[TopViewController alloc]
                         initWithNibName:@"TopViewController"
                         bundle:nil];
        topViewController.delegate = self;
    }
    [self page:topViewController.view];
}

viewDidLoadで、自分自身のDelegateを自分のクラス(親)を受け渡す。

ページ切替のメソッドpageLogin、pageTopでそれぞれのViewControllerを作成し、Delegateに親クラスを受け渡す。

LoginViewController.h

#import "YosouViewController.h"

@interface LoginViewController : UIViewController<YosouViewControllerDelegate> {
    id <YosouViewControllerDelegate> delegate;
}

@property (nonatomic, assign) id delegate;
-(IBAction) loginPressed:(id)sender;

YosouViewControllerDelegateを継承(っていうのか?)して、YosouViewControllerDelegateを定義する。 loginPressedはログインボタンクリックのメソッド。

LoginViewController.m

#import "LoginViewController.h"
#import "YosouViewController.h"

@implementation LoginViewController
@synthesize delegate;

....

#pragma mark -
#pragma mark IBAction Methods
-(IBAction) loginPressed:(id)sender {
    NSLog(@"LoginViewController loginPressed");
    [delegate pageTop];
}

YosouViewControllerDelegateでDelegateされているので、delegateプロパティを経由してpageTopにアクセスできる。

まとめ

つうわけで、自分なりにまとめ。 Delegateを利用すれば、クラス間のアクセスはインスタンスを無駄に作成せずアクセスできる。

結局はシングルトンでクラスインスタンスを受け渡しているのと同じことをしてる(?)と思うが、 Delegateと明示した方がわかりやすいすね。 またextends(継承)する方法もあるが、これは上下関係が必須となってしまう。 Delegateは、上下関係なく縦横無尽にクラスにアクセスできるのが良い点。