Cocoaのイベント処理について

1,257 views
Skip to first unread message

hiro

unread,
Feb 10, 2009, 5:44:57 AM2/10/09
to cocoa-dev-japan
初めて投稿させていただきます。
Cocoaに触り始めて数週間の初心者です。

CocoaでGUIを作成する際、Interface BuilderでGUI(例えばNSButton)とアクションを結びつけると、
NSButtonのクリックイベントをアクションで処理することができると思いますが。しかし、NSButton上でのmouseUpイベントや、
mouseEnteredイベントを処理するにはどうすればよいのでしょうか?

調べた限り、Interface Builderで線を引っ張った時、NSControlのsetTarget: setAction:を実行してお
り、イベントの種類を指定してイベントを処理するセレクタを登録することはできなそうでした。

そこで、NSButtonを継承した次のようなMyButtonを作成し、Interface BuilderでWindowに配置したボタンを選択し
た後、ClassをNSButtonからMyButtonに変更して試してみましたが、mouseEnteredメソッドは呼ばれませんでした。

------------------------------------------------------------------------------------------
@interface MyButton : NSButton {

}

@implementation MyButton

//マウスエンターイベントの処理
- (void)mouseEntered:(NSEvent*)event {
NSLog(@"mouse entered");
}

------------------------------------------------------------------------------------------

NSButtonはNSResponderを継承しているはずなので、これで呼ばれない理由がわかりません。

また、この例のように、1つのコンポーネントで発生する複数種類のイベントを、別々のメソッドと関連づけて処理することは普通のことなのではないかと思
うのですが、こういった処理をする場合、Cocoaプログラミングの常識では、どのようにするのでしょうか(Visual Studioなんかだと、イ
ベントごとにメソッドが自動生成されますが...)?

ようやくInterface Builderの使い方も理解したばかりで、Objective-Cもまだまだ初心者の域を出ていない未熟者ですが、どう
かご教授ください。

よろしくお願いいたします。

百舌鳥

unread,
Feb 11, 2009, 12:13:02 AM2/11/09
to cocoa-d...@googlegroups.com
hiroさん、みなさん初めまして、僕も初投稿です。
僕もcocoa は初心者なんですが、最近同じことをやってなんとかうまくいったの
で書いておきます。僕の環境はXCode2 でOSX 10.4.11です。

mouseEnter, mouseLeave のイベントを捕まえるためには、そのview(NSBottun
とかNSTextFieldも)の 

addTrackingRect:owner:userData:assumeInside:

をはじめに呼んでやる必要があります。下のような感じです。

NSTrackingRect *trackingRect;
--------
trackingRect = [self addTrackingRect:[self bounds] owner:self
userData:NULL assumeInside:NO];


でもしそのボタンなりフィールドを含むウィンドウがサイズ変更をする場合はそ
の度に、上の trackingRect を更新する必要があります。

[self removeTrackingRect:trackingRect];
trackingRect = [self addTrackingRect:[self bounds] owner:self
userData:NULL assumeInside:NO];

僕が参考にしたHPは以下です。
http://xcatsan.blogspot.com/2008/02/blog-post_3361.html

hiro

unread,
Feb 11, 2009, 10:41:50 PM2/11/09
to cocoa-dev-japan
百舌鳥さん

ご回答ありがとうございます.

簡単なアプリケーションを作成し,動作を確認しました.また,参考URLありがとうございます.

> mouseEnter, mouseLeave のイベントを捕まえるためには、そのview(NSBottun
> とかNSTextFieldも)の
>
> addTrackingRect:owner:userData:assumeInside:
>
> をはじめに呼んでやる必要があります。

これらのことからまとめると,

・Cocoaでは,Actionに登録できるセレクタは1つで,イベントごとに異なるセレクタを登録できる仕組みは用意していない.
・仮にActionに設定したセレクタ以外で個々のイベントを取得するには,NSViewを継承するコンポーネントを継承し,独自クラスを作成してなん
とかするしかいない.

ということになりますかね.

少し疑問だったのは,addTrackingRect:owner:userData:assumeInside:はNSViewで提供されているメ
ソッドなので,GUIコンポーネントはNSViewを継承しているからよいのですが,NSResponderを直接継承してmouseDown:イベン
トを受け取り場合などはどうするんでしょうね?単にNSResponderを継承し,delegateとかすればよいのですかね?

この辺は深追いしません(必要なときにまた勉強するということで)

重ね重ね,ありがとうございました!


kawai kazuo

unread,
Feb 12, 2009, 10:09:35 AM2/12/09
to cocoa-d...@googlegroups.com
hiroさん、お役に立てたようでよかったです^^
僕もMacのプログラミングの経験は殆どなくて、WindowsのDelphiなどをもっぱら
やってましたから、まだcocoaの流儀になれないところがあります。独立した機
能をもったオブジェクトを考えて、その一部の機能を他にデリゲートするという
考え方はそれなりに一貫しててすっきりします(昔、HyperCardでボタンやフィー
ルドのスクリプトを別々に書いていたのを思い出します)が、一つのイベントだ
けをactionとして特別扱いするのがちょっとややこしい話ですね。最初はわかり
やすいけど、他のイベントを扱おうとすると急にむずかしくなるように思います。

> 少し疑問だったのは,addTrackingRect:owner:userData:assumeInside:はNSViewで提供されているメ
> ソッドなので,GUIコンポーネントはNSViewを継承しているからよいのですが,NSResponderを直接継承してmouseDown:イベン
> トを受け取り場合などはどうするんでしょうね?単にNSResponderを継承し,delegateとかすればよいのですかね?

僕の理解している所では、画面上にあらわれるGUIの部品はたいてい、NSWindow
かNSViewのどちらかを継承することになると思います。NSResponderはその両方
にイベント処理の枠組みを提供するための、abstractなclassと位置づけられて
いるんだと思います。だからGUIの部品であればそこから直接継承するよりも、
上記二つのものから継承するのが簡単ということじゃないですか?> 百舌鳥さん

Yanagisawa

unread,
Feb 14, 2009, 10:19:32 AM2/14/09
to cocoa-d...@googlegroups.com
On Feb 11, 2009, at 7:41 PM, hiro wrote:

> ・Cocoaでは,Actionに登録できるセレクタは1
> つで,イベントごとに異なるセレクタを登録できる仕組みは用意
> していない.
> ・仮にActionに設定したセレクタ以外で個々のイベントを
> 取得するには,NSViewを継承するコンポーネントを継承
> し,独自クラスを作成してなん
> とかするしかいない.

action は任意のイベントを拾うような仕組みではないです。一般的
なイベントに関しては、delegate, notification, binding
等が用意されている場合、サブクラス化は不要です。

マウスのトラッキングですが、
最初のメールでは「mouseEntered: を実装したが呼ばれな
い」という話でしたが、逆にそれだけで動くなら、それは普段から
デフォルトの mouseEntered: 呼ばれているということにな
り、非常に重い物になってしまうと思います。

> ということになりますかね.

最初に「Cocoa では」と断られていますが、他の環境では
mouseEntered: に対応する部分を実装するだけでマウス移動のイベ
ントが拾えるようなものがあるということでしょうか。もしそうな
ら参考までに教えていただけると。

> 少し疑問だったのは,addTrackingRect:owner:userData:assumeInside:
> はNSViewで提供されているメ
> ソッドなので,GUIコンポーネントはNSViewを継承し
> ているからよいのですが,NSResponderを直接継承して
> mouseDown:イベン
> トを受け取り場合などはどうするんでしょうね?単に
> NSResponderを継承し,delegateとかすればよいのですかね?

NSResponder に関しては responder chain というものがあり
ます。
一般的な Cocoa の UI のオブジェクトは自分を含む
NSView がいるのでそれにお願いするという手があると思います。

Yanagisawa


hiro

unread,
Feb 15, 2009, 2:03:17 AM2/15/09
to cocoa-dev-japan
Yanagisawa さん

ご教授ありがとうございます.

> action は任意のイベントを拾うような仕組みではないです。一般的
> なイベントに関しては、delegate, notification, binding
> 等が用意されている場合、サブクラス化は不要です。

なるほど.delegateは確かに便利な機構だと思いますし,notificationもよく考えられていると感じています(まだその便利さを体験す
るほどいじっていないので具体的な実感は伴っていないのですが).

この話題とはそれてしまうかもしれないのですが,delegateについて,皆様に2点ほど伺いたいことがあります.

まず1点目ですが,一般に,Delegateメソッドは非公式プロトコルで実現されているという記述を目にします.
実際,上のメソッドは,NSApplication.hに次のようなカテゴリで定義されています.

@interface NSObject(NSApplicationNotifications)
- (void)applicationDidFinishLaunching:(NSNotification *)notification;
以下続く..
@end


したがって,NSApplicationの実装は,

@implementation NSApplication(NSApplicationNotifications) {

- (void) applicationDidFinishLaunching:(NSNotification*)aNotification
{
if(!delegate) {
[delegate applicationDidFinishLaunching:(NSNotification*)
aNotification];
}
以下続く..
}

このようなメソッドがNSApplicationNotificationsカテゴリで定義されたメソッドの数だけ定義されていると考えてよいのでしょ
うか?

次に2点目ですが,Bluetooth APIのDelegateメソッドの中で,IOBluetoothRFCOMMChannelには,
rfcommChannelData:data:lengthなど,
いくつかのメソッドが@protocol指示子で記述されています.

---------------------------------------------------------------------------------
IOBluetoothRFCOMMChannel.hの376行目付近

@protocol IOBluetoothRFCOMMChannelDelegate
- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*) rfcommChannel
data:(void *)dataPoinster length:(size_t)dataLength;
以下続く..
@end

---------------------------------------------------------------------------------

ところが,IOBluetoothRFCOMMChannelの定義は

@interface IOBluetoothRFCOMMChannel : IOBluetoothObject

であり,IOBluetoothObjectの定義は

@interface IOBluetoothObject : NSObject

なので,IOBluetoothRFCOMMChannelDelegateを採用していないと思うのです.

しかし,実際にIOBluetoothRFCOMMChannelのsetDelegateを実行し,Delegateメソッドを定義すると
定義したメソッドが呼ばれるので,機能することは確認しています.

これはいったいどういう仕組みで動いているのでしょうか?

当方の予想は,単に@protocol指示子で開発者にわかりやすいようにメソッドの定義だけを示したにすぎないのでは?
と思うのですが...

また,ついでですが,NSApplicationの場合はDelegate MethodとしてXcodeのヘルプにちゃんと記載されているのです
が,
IOBluetoothRFCOMMChannelには一切記載されていません.よって,Working with Bluetooth Device
などのドキュメント,
サンプルプログラム,ヘッダファイルを見てその存在を知ったのですが,クラスリファレンスに記載されていないDelegate
メソッドが存在するような例はMacプログラミングの場合普通にあることなのでしょうか?


> マウスのトラッキングですが、
> 最初のメールでは「mouseEntered: を実装したが呼ばれな
> い」という話でしたが、逆にそれだけで動くなら、それは普段から
> デフォルトの mouseEntered: 呼ばれているということにな
> り、非常に重い物になってしまうと思います。
>
> > ということになりますかね.
>
> 最初に「Cocoa では」と断られていますが、他の環境では
> mouseEntered: に対応する部分を実装するだけでマウス移動のイベ
> ントが拾えるようなものがあるということでしょうか。もしそうな
> ら参考までに教えていただけると。

たとえばC#なんかでは,

MyButton.Click += new System.EventHandler(this.clickEventHandler);

のように,オブジェクト.イベント名にイベントハンドラを関連付けることで,イベントが起きた時にハンドラが呼ばれるようになります.
もちろん,コンポーネントごとに拾えるイベントは異なりますが,ボタンオブジェクトでも,MouseOverやMouseLeaveなど,50個
くらいのイベントを拾えます.

なので,今回のNSButtonの場合も,同様のことができるのではないかと考えたわけです...

この考えに一番近かったのがdelegateだったので,最初はイベントごとにdeleagateを設定できるのではと考えたのですが,
今回の要求に対しては(NSButtonのmouseEnteredイベントを取る),NSButtonを継承した独自クラスを
使うしかないですよね(delegateでなんとかできれば一番良いです.Notificationをうまく使うとできる??勉強不足ですみませ
ん)?


> NSResponder に関しては responder chain というものがあり
> ます。
> 一般的な Cocoa の UI のオブジェクトは自分を含む
> NSView がいるのでそれにお願いするという手があると思います。

おっしゃる通り,CocoaのGUIコンポーネントは必ずNSViewを継承するので,実際にはそれが一番だと思います.

以上,長くなって済みませんが,よろしくお願いいたします.

wang

unread,
Feb 15, 2009, 2:12:12 AM2/15/09
to cocoa-dev-japan
On 2月12日, 午後12:41, hiro <hiro...@java2.jp> wrote:
> ・Cocoaでは,Actionに登録できるセレクタは1つで,イベントごとに異なるセレクタを登録できる仕組みは用意していない.
> ・仮にActionに設定したセレクタ以外で個々のイベントを取得するには,NSViewを継承するコンポーネントを継承し,独自クラスを作成してなん
> とかするしかいない.

「Cocoaでは」という話ではなく「NSButton」ではですね。
Cocoaというフレームワークでは、効率的に開発をするために機能特化した部品を実装しています。
目的にあったクラスを使うことで開発が効率ができます。

NSButtonは、マウスクリックに反応してなにかアクションを実行しその実行時にユーザーにわかりやすいリアクションを返すことに特化したクラスで
す。

ですから、一般的にマウスがクリックされていないときにどこにあるかなどは関知する必要がありません。
逆に関係ないことまで処理をしていたりインタフェースを用意しているとわかりにくいクラスになったり実行が遅くなったりすることになるかもしれませ
ん。

ボタンとしての機能ではなく特殊なものを実装する場合には特殊なクラスを実装したほうが効率的という場合があるでしょう。



さて、mouseEntered:やmonseExited:に対応した処理をしたいというのはどういう目的なのでしょうか?
その目的からアプローチしたほうが正しい解を得られるかもしれません。

ボタン上にマウスカーソルがあるかどうかを知りたい場合というのはマウスカーソルの形状をボタンにあわせて変えたい場合かツールチップを表示するという
ことしか思いつきません。

ツールチップの表示にはそのためのインタフェース(setToolTip:)が用意されていますし、マウスカーソルの形状変更にもそのためのインタ
フェース(addCursorRect:cursor:)があります。

そちらで対応できるのであれば独自に実装するのではなくフレームワークで用意されたインタフェースを使った方がいいでしょう。

hiro

unread,
Feb 15, 2009, 3:56:20 AM2/15/09
to cocoa-dev-japan
wangさん

ご回答ありがとうございます.

> 「Cocoaでは」という話ではなく「NSButton」ではですね。
> Cocoaというフレームワークでは、効率的に開発をするために機能特化した部品を実装しています。
> 目的にあったクラスを使うことで開発が効率ができます。
>
> NSButtonは、マウスクリックに反応してなにかアクションを実行しその実行時にユーザーにわかりやすいリアクションを返すことに特化したクラスで
> す。
>
> ですから、一般的にマウスがクリックされていないときにどこにあるかなどは関知する必要がありません。
> 逆に関係ないことまで処理をしていたりインタフェースを用意しているとわかりにくいクラスになったり実行が遅くなったりすることになるかもしれませ
> ん。

なるほど!納得です.そもそもSwingや.Netとは思想が違う,と考えた方がよさそうです.

「1つのコンポーネントはある処理に特化してつくるべき」ということですね.だとすると,Target-Actionの理解が納得できます.


> ボタンとしての機能ではなく特殊なものを実装する場合には特殊なクラスを実装したほうが効率的という場合があるでしょう。
>
> さて、mouseEntered:やmonseExited:に対応した処理をしたいというのはどういう目的なのでしょうか?
> その目的からアプローチしたほうが正しい解を得られるかもしれません。

すみません.特に何か目的があったわけではなく,Cocoaの勉強のためいろいろいじっていた時に疑問に思ったのでこちらでご質問出せていただいた次第
です.

納得のいくご説明,ありがとうございました!!

百舌鳥

unread,
Feb 16, 2009, 12:41:11 AM2/16/09
to cocoa-d...@googlegroups.com
wangさん、こんにちは。

> さて、mouseEntered:やmonseExited:に対応した処理をしたいというのはどういう目的なのでしょうか?
> その目的からアプローチしたほうが正しい解を得られるかもしれません。
>
> ボタン上にマウスカーソルがあるかどうかを知りたい場合というのはマウスカーソルの形状をボタンにあわせて変えたい場合かツールチップを表示するという
> ことしか思いつきません。

僕が現在作成中のアプリは韓国語動詞活用の練習ソフトでして、動詞と語尾を結
びつけて正しい活用形をつくりだす練習のためのツールです。で、正しい活用形
(解答)を表示するNStextFieldを用意しているんですが、それは普段は表示され
ず、表示表示を切り替えるボタンを別に用意しています。そのボタンをクリック
するとそのtextFieldの表示非表示がトグルするようにしています。
ただ一時的に答えを確かめたいという時にはボタンの上にマウスを近づけた時だ
けそのtextFieldが表示されるという動作を追加していて、その時に、
mouseEnter, mouseExitedのイベントを拾って使用しています。toolTipは若干動
作が遅くてこの方がいいかなと思っています。

--
百舌鳥
http://homepage2.nifty.com/mozu

Reply all
Reply to author
Forward
0 new messages