コントローラに定義したメソッドをタッチで呼び出したくて奮闘した。
デリゲートって、なんか小難しい技なのかと思ってたけど、単にコントローラとかのインスタンスのポインタをビューのインスタンス変数に入れておくってだけっぽい。
正確には違うのかもしれないけど、多分そんな感じ。
先週末辺りから会社でiPhoneアプリ作ってる。
面白い。
ソース
登場人物は、2つ。
・Controller => UIViewControllerを継承したRootViewController。
・View => UIViewを継承したRootView。
RootView.delegateにRootViewControllerを代入。
RootViewでタッチイベントを受け取る。
RootViewからRootView.delegateに代入されてるRootViewControllerのメソッドを呼び出す。
RootViewController.h
#import <UIKit/UIKit.h> #import "RootView.h" #import "TouchesDelegate.h" @interface RootViewController : UIViewController <TouchesDelegate> { } @end
RootViewController.m
#import "RootViewController.h" @implementation RootViewController /* // The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { // Custom initialization } return self; } */ // Implement loadView to create a view hierarchy programmatically, without using a nib. - (void)loadView { RootView *contentView = [[RootView alloc] initWithFrame:CGRectMake(0,20,320,460)]; [contentView setDelegate:self]; self.view = contentView; [contentView release]; } /* // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { [super viewDidLoad]; } */ /* // Override to allow orientations other than the default portrait orientation. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } */ - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } /* TouchesDelegate */ - (void)view:(UIView*)view touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { NSLog(@"delegate touchesBegan"); } - (void)view:(UIView*)view touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { NSLog(@"delegate touchesMoved"); } - (void)view:(UIView*)view touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { NSLog(@"delegate touchesEnded"); } - (void)view:(UIView*)view touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { NSLog(@"delegate touchesCancelled"); } - (void)dealloc { [super dealloc]; } @end
RootView.h
#import <UIKit/UIKit.h> #import "TouchesDelegate.h" @interface RootView : UIView { id<TouchesDelegate> delegate; } @property (nonatomic, retain) id delegate; @end
RootView.m
#import "RootView.h" @implementation RootView @synthesize delegate; - (id)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { // Initialization code } return self; } /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ /* touch */ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesBegan"); [delegate view:self touchesBegan:touches withEvent:event]; } - (void)touchesMoved: (NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesMoved"); [delegate view:self touchesMoved:touches withEvent:event]; } - (void)touchesEnded: (NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesEnded"); [delegate view:self touchesEnded:touches withEvent:event]; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesCancelled"); [delegate view:self touchesCancelled:touches withEvent:event]; } - (void)dealloc { [super dealloc]; } @end
TouchesDelegate.h
#import <UIKit/UIKit.h> @protocol TouchesDelegate @optional - (void)view:(UIView*)view touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event; - (void)view:(UIView*)view touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event; - (void)view:(UIView*)view touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event; - (void)view:(UIView*)view touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event; @end
デバッグログ
ちゃんとデリゲートされてる。
2010-08-03 04:15:32.991 TouchTest[1834:207] touchesBegan 2010-08-03 04:15:32.992 TouchTest[1834:207] delegate touchesBegan 2010-08-03 04:15:33.170 TouchTest[1834:207] touchesMoved 2010-08-03 04:15:33.170 TouchTest[1834:207] delegate touchesMoved 2010-08-03 04:15:33.237 TouchTest[1834:207] touchesMoved 2010-08-03 04:15:33.237 TouchTest[1834:207] delegate touchesMoved 2010-08-03 04:15:33.321 TouchTest[1834:207] touchesMoved 2010-08-03 04:15:33.321 TouchTest[1834:207] delegate touchesMoved 2010-08-03 04:15:33.393 TouchTest[1834:207] touchesEnded 2010-08-03 04:15:33.394 TouchTest[1834:207] delegate touchesEnded
参考URL
【コラム】実践! iPhoneアプリ開発 (23) タワーディフェンスゲームの作り方 (6) - キャノンを配置する | エンタープライズ | マイコミジャーナル
http://journal.mycom.co.jp/column/iphone/023/index.html
追記 2010-08-03 17:21:38
プロトコルの@optional指定したメソッドを実装しなかった時にエラーが出た。
2010-08-03 16:51:33.240 TouchTest[3343:207] touchesBegan 2010-08-03 16:51:33.247 TouchTest[3343:207] *** -[RootViewController view:touchesBegan:withEvent:]: unrecognized selector sent to instance 0x3912fc0 2010-08-03 16:51:33.248 TouchTest[3343:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[RootViewController view:touchesBegan:withEvent:]: unrecognized selector sent to instance 0x3912fc0' 2010-08-03 16:51:33.248 TouchTest[3343:207] Stack: ( 29291611, 2480882953, 29673531, 29243124, 29095618, 9600, 2825523, 2734024, 2760801, 37387609, 29076352, 29072456, 37381653, 37381850, 2764719, 8198 )
respondsToSelector
実装されているかチェックしてから実行しないといけないらしい。
nullのオブジェクトのメソッドを呼んでも例外を発生させないObjective-Cの事だから、きっと大丈夫と思ってたら、駄目だった。
ゆるいのは、オブジェクトそのものに対してだけらしい。
メソッドの有無は、respondsToSelectorを使って調べる。
RootView.mを変更。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesBegan"); if ([delegate respondsToSelector:@selector(view:touchesBegan:withEvent:)]) { [delegate view:self touchesBegan:touches withEvent:event]; } } - (void)touchesMoved: (NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesMoved"); if ([delegate respondsToSelector:@selector(view:touchesMoved:withEvent:)]) { [delegate view:self touchesMoved:touches withEvent:event]; } } - (void)touchesEnded: (NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesEnded"); if ([delegate respondsToSelector:@selector(view:touchesEnded:withEvent:)]) { [delegate view:self touchesEnded:touches withEvent:event]; } } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"touchesCancelled"); if ([delegate respondsToSelector:@selector(view:touchesCancelled:withEvent:)]) { [delegate view:self touchesCancelled:touches withEvent:event]; } }
コンパイルしたら、こんなwarningが出た。
'-respondsToSelector:' not found in protocol
respondsToSelectorはNSObjectで定義されているメソッドなので、プロトコルにNSObjectを継承させる。
TouchesDelegate.hを変更。
@protocol TouchesDelegate <NSObject>
参考URL
iPhoneアプリ開発、その(120) 一人時間差攻撃〜!|テン*シー*シー
http://ameblo.jp/xcc/entry-10325508764.html