Objective-C デリゲートに関して

最初、デリゲート(Delegate)とはObjective-Cまたは、iOS SDKの何か特別な機能のことだと思っていた。どうやらそう言うことでは無いらしく、単にプロトコルという機能を使ったオブジェクト間のやり取りの事を言うらしい。委任とか難しい言葉を使われると分からなくなる。
以下初心者の自分なりに噛み砕いてまとめてみた。

デリゲートとは

あるオブジェクトAとオブジェクトBがある。
Aで起こったアクションに対し、Bで何かイベントを起こしたいとする。しかし、BはAでアクションが起こった事が分からない。そこでBにAでアクションが起こった事を知らせる方法を実装する。その結果、Aのアクションをトリガーにして、Bでイベントを起こせる様にする。このことをデリゲートと言うらしい。

デリゲートを使うには

デリゲートを行う為にObjective-Cにあるプロトコルという機能を用いて、オブジェクトBにオブジェクトAのアクションに対するデリゲートメソッドを実装していく。

プロトコルとは簡単に言えば自分のクラスに無いメソッドを新たに実装するための方法の一つである。
デリゲートメソッドはAが起こすアクションに応じて呼び出されるメソッドで、プロトコルを用いてBに実装する。このデリゲートメソッド内にBで起こしたいイベントを記述する。

これにより、『Aのアクション→デリゲートメソッド→Bのイベント』が実現する。


簡単な例

テキストフィールドに文字が入力され、リターンキーが押されると、ラベルにテキストフィールドで入力された文字が表示されるプログラムを考える。上記のモデルと照らし合わせると、オブジェクトAがテキストフィールドで、そのトリガーとなるアクションがリターンキーを押すことになる。そしてオブジェクトBにあたるラベルにイベントとして文字が表示される。

  1. MyTextFieldクラスのtextField_AとMyLabelクラスのlabel_Bを生成し、画面に表示されるようにする。
  2. MyAppDelegate.mの textField_A.delegate = label_B; で、textField_Aのデリゲート先にlabel_Bを指定する。
  3. MyLabelクラスのヘッダーファイルMyLabel.hで、
    @interface MyLabel : UILabel <UITextFieldDelegate> と記述し、プロトコルとして 
    <UITextFieldDelegateを宣言する。UITextFieldDelegateにはUITextField用の色々なアクションに対応するデリゲートメソッドがそれぞれ用意されており、MyLabelクラスのプロトコルとして宣言することにより、MyLabelクラスでUITextFieldDelegateメソッドを実装できるよようになる。
  4. MyLabelクラスの実装ファイルMyLabel.mでUITextFieldDelegateに用意されたデリゲートメソッドの内、リターンキーが押された時に呼ばれるメッソド - (BOOL)textFieldShouldReturn:(UITextField *)textField にリターンキーが押された時のlabel_Bのイベントを記述する。このメソッドの引数(UITextField *)textFieldが呼び出し元のtextField_Aになっている。

以下ソースコード
*Xcode4.5.2を使用
*ARCを使用
*EmptyApplicationから、IBを使用せずに作成

MyAppDelegate.h
#import <UIKit/UIKit.h>
#import "MyLabel.h"
#import "MyTextField.h"

@interface MyAppDelegate : UIResponder <UIApplicationDelegate,UITextFieldDelegate>

@property MyTextField* textField_A;
@property MyLabel* label_B;

@property (strong, nonatomic) UIWindow *window;


@end

MyAppDelegate.m
#import "MyAppDelegate.h"

@implementation MyAppDelegate
@synthesize textField_A, label_B;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
    //textField_Aとlabel_Bを生成
    textField_A = [[MyTextField alloc] initWithFrame:CGRectMake(20, 130, 200, 50)];
    label_B = [[MyLabel alloc] initWithFrame:CGRectMake(20, 50, 200, 50)];
    
    //textField_Aとlabel_Bをウィンドウに表示
    [self.window addSubview:textField_A];
    [self.window addSubview:label_B];
    
    //textField_Aのdeligateプロパティをlabel_Bに → textField_Aのデリゲート相手をlabel_Bにする
    textField_A.delegate = label_B;
    
    
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

@end

MyTextField.h
#import <UIKit/UIKit.h>

@interface MyTextField : UITextField 

@end

MyTextField.m
#import "MyTextField.h"

@implementation MyTextField

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.borderStyle = UITextBorderStyleRoundedRect;
    }
    return self;
}

@end

MyLabel.h
#import <UIKit/UIKit.h>

//MyLabelクラスにUITextFieldDelegateプロトコルを読み込む
@interface MyLabel : UILabel <UITextFieldDelegate>

@end

MyLabel.m
#import "MyLabel.h"

@implementation MyLabel

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
    }
    return self;
}

//UITextFieledDelegateのメッソドの一つ、テキストフィールドのリターンが押された時に呼ばれる
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    //ラベルに表示されるテキストをテキストフィールドのテキストにする
    self.text = textField.text;
    return YES;
}

@end

コメント

  1. デリゲートの明快な解説を求めてネットを彷徨っていたところ本ページにたどり着きました.
    説明文,サンプルコードともに非常にわかりやすかったです.

    返信削除

コメントを投稿