ファーストレスポンダとは?

macOS Swift

ファーストレスポンダとは?

macOS Mojava 10.14.6 / Xcode 11.3.1 / Swift 5.0
Apple公式によると次の通り
The first responder is usually the first object in a responder chain to receive an event or action message. In most cases, the first responder is a view object that the user selects or activates with the mouse or keyboard.
「ファーストレスポンダとは、一般的には、レスポンダーチェーンの中で、イベントやアクションメッセージを受け取る最初のオブジェクトのことである。ほとんどの場合それは、ユーザがマウスやキーボードにより、選択したり実行したりすることができるビューオブジェクトのことである」
ウィンドウアプリケーションは、一般的には、テキストフィールドやボタンといったマウスやキーボードからのイベントを受け付ける複数のオブジェクトから構成される。アプリケーションがアクティブであるとき、ある一時点で、イベントを受け付けることができるオブジェクトは、その中の一つだけである。これをファーストレスポンダと言う。
イベントを受け付ける、すなわちファーストレスポンダになり得るクラスは NSResponderクラスから派生している。それは NSWindowクラス、NSVewクラスであり、NSVewクラスは、NSControlクラスを経由して、NSTextField、NSButton、NSTableViewといったコントロール系のクラスに派生する。

ファーストレスポンダになる

ファーストレスポンダになり得るオブジェクトは、オブジェクトをマウスでクリックするとファーストレスポンダになる。
アプリケーションに複数のファーストレスポンダになり得るオブジェクトが配置されている場合、タブキーによってファーストレスポンダが移動する。例えば、複数のテキストフィールドがあるとき、その時点で値が入力できるテキストフィールド(すなわちカーソルが点滅し、通常は青色の枠線・フォーカスリングで囲まれている)は、タブキーの押下によりあらかじめ決まった順番で移動する。
ちなみに Xcodeで自前のアプリケーションを作成し、ビューに複数のテキストフィールドを配置したとき、何の設定も行わなければ、自動的に、まず左から右へ、次に上から下へと物理的なコントロールの配置にしたがってファーストレスポンダーの移動順が決まる。他のツールで見られるような、コントロールの貼り付け順ではない。

指定したオブジェクトをファーストレスポンダにする

NSwindowクラスの makeFirstResponderメソッドは、ウィンドウに配置されているオブジェクトをファーストレスポンダにする。
ファーストレスポンを切り替えるサンプルプログラム
Nextボタンをクリックするたびに、左右のテキストフィールドの間でファーストレスポンダーが移動する。タブキーで複数のコントロールの間をファーストレスポンダが移動するのと同じ動作になる。
[image3]
NSTextFieldクラスのサブクラスを作成する。赤字の部分がカスタマイズで追加したところである。
自身がファーストレスポンダであるか否かを示す isFirstResponderプロパティを追加し、NSResponderクラスのプロパティとメソッドをオーバーライドする。これらはファーストレスポンダの要になるものなので少し解説する。
acceptsFirstResponderプロパティは、自身がファーストレスポンダとして振る舞えるか否かを指定する。(デフォルト値は true)
makeFirstResponderメソッドの動きは、まず、現在のファーストレスポンダーに対して、ファーストレスポンダーを放棄するよう要求する。それが受け入れられた場合、次に、これからファーストレスポンダにしようとするオブジェクトに対して、ファーストレスポンダになるよう要求するというものである。
ファーストレスポンダを放棄するよう要求されたオブジェクトでは、resignFirstResponderメソッドが起動する。放棄していい場合は trueを返す。拒否する場合は falseを返し、そのままファーストレスポンダであり続ける。(デフォルト値は true)
ファーストレスポンダになるよう要求されたオブジェクトでは、becomeFirstResponderメソッドが起動する。要求を受け入れファーストレスポンダになる場合は trueを返し、拒否する場合は falseを返す。(デフォルト値は true)
オーバーライドしているメソッドの戻り値を変えれば、挙動を変得ることができる。
なお、先に示した acceptsFirstResponderプロパティが falseであれば、上記の働きはそもそも無効である。
AppDelegateクラスの実装
ボタンのクリックでファーストレスポンダを切り替える。テキストフィールドオブジェクトは配列にし、配列の添字によって移動順をコントロールしている。
ひとつ重要な点は、上記のプロパティやメソッドの戻り値をいかに変更しようと、テキストフィールドをマウスでクリックすると、それは必ずファーストレスポンダになるということである。今まで述べてきたことは、あくまでも makeFirstResponderメソッドに対する動きということになる。

レスポンダチェーン

レスポンダチェーンは、NSResponderオブジェクトの連鎖という意味であり、イベントハンドラの探索経路である。具体的な例で説明しよう。

(1)クラスの継承関係に基づいたレスポンダチェーン

[image1]
NSViewクラスのサブクラス ViewAクラスと ViewBクラスを作成する。ViewBクラスは ViewAクラスから継承する。それぞれのクラスにマウスダウンイベントを捉える mouseDownメソッドを実装し、print文を実行したあと、supaerクラスの mouseDownメソッドを呼ぶ。
ViewBクラスのビューオブジェクトを作成し、コンテントビューに貼り付ける。 ビューをクリックすると、まず ViewBクラス(子供)の mouseDownメソッドが動作し、次に ViewAクラス(親)のメソッドが動く。
さてこのとき、ViewBクラス(子供)に mouseDownメソッドを実装していなければどうなるだろう。 マウスダウンイベントは ViewBクラスに自分用のイベントハンドラが見つからないので、継承したクラスを遡って、目当てのメソッドを探しにいく。もしあればそれを実行し、なければそのまた親クラスを探しにいく。できる限り遡るが、メソッドが見つからず終わることも多い。
このときの、イベントが自分用のイベントプロシージャ(メソッド)を探索する経路がレスポンダチェーンである。
なお本例では、mouseDownメソッドの中で、suoerクラスの mouseDownメソッドを再び呼んでいる。これはカスタムクラスのメソッドの実行によって、元々あったレスポンダチェーンが途切れることを避けるための対応である。これによりこのあとも本来のイベントハンドタの探索と同じプロセスが続く。

(2)ビューの重なりに基づいたレスポンダチェーン

[image2]
NSViewクラスのサブクラスを作成する。ViewAクラスは、名前をプロパティに持ち、背景色を変えることができる。マウスダウンイベントを捉える mouseDownメソッドを実装し、print文を実行したあと、supaerクラスの mouseDownメソッドを呼ぶ。
ViewAクラスのオブジェクト myView1と myView2を作成する。myView1はコンテントビューに貼り付け、myView2は myView1に貼り付け、ビューを重ねる。myView2をクリックすると、まず myView2の mouseDownメソッドが呼ばれ、次に myView1の mouseDownメソッドが呼ばれる。
ビューの重なりに基づいたレスポンダチェーンの動きは、上記の「(1)クラスの継承関係に基づいたレスポンダチェーン」で記述した動きと全く同じである。レスポンダチェーンの観点から見ると、ビューの重なりと、クラスの親子関係に違いはないようだ。
レスポンダチェーンを論じるとき、どちらのケースを話題としているか明確にしておいかないと、やや混乱するかもしれない。

[補足]

値の入力がないボタンは、システム環境設定により動作が異なってくる。キーボード → ショートカットで、キーボードアクセスを「すべてのコントロール」にすると、ボタンもファーストレスポンダになり得て、テキストフィールドと同じようにとタブキーで移動する。ただし、ボタンをクリックしてもファーストレスポンダにならない。ボタンというものの性質上そういうものか。