9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

First Responderについて理解する

Posted at

はじめに

iOSではアプリ起動時に外的作用であるイベントを待ち続け、

イベントが来たら1つずつ処理をするイベントループと呼ばれる状態が作られる。

イベントの例としては、画面のタッチ、端末のシェイク、サーバからのプッシュ通知などがある。

これらの通知は一時的にイベントキューという場所に格納され、先入先出の順でイベントループにより処理がおこなわれる。

アプリケーションの共通部分となるUIApplicationオブジェクトはこのイベントループを管理し、

受け取ったイベントを適所にディスパッチ(割り振り)している。

FirstResponderとは

iOSがイベントを検知したときに

一番はじめにイベント処理を試みるUIResponderオブジェクト。

イベント発生時、まずFirstResponderにイベントがディスパッチされ、

FirstResponderでイベントを捌くことができなかった場合に、

Responder Chain(詳細後述)を辿って、処理できるResponderを探す。

FirstResponderになるためには

canResignFirstResponderプロパティをtrueを返すようにし、

becomeFirstResponder()メソッドでFirstResponderになることを宣言する。

import UIKit

class SampleViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // View表示時にFirstResponderになることを宣言 
        self.becomeFirstResponder()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // Viewが非表示時にFirstResponderをやめる
        self.resignFirstResponder()
        
        
    }
    
    // このViewControllerがFirstResponderになれるようにする
    override var canBecomeFirstResponder: Bool {
        return true
    }
    
}

そもそもUIResponderとは

イベントに応答して処理するための抽象的なインターフェース

Apple公式

より具体的に書くと、

「UILabelなどUIControlのサブクラスではないUI部品において、

iOSが検知したtouchイベントなどを処理するためのクラス」

UIViewやUIViewController、UIViewを継承しているUIWindow、AppDelagate、UIApplicationなどは、UIResponderを継承している。

そのため、UIButtonのように自分自身がタッチされたことを検知できる仕組みを持っていなくても、

UIResponderのメソッドをオーバーライドすることでイベントを処理することができる。

例えばtouchイベントの場合

UIResponderのサブクラスで以下のようなメソッドをオーバーライドすることができる。

touchesBegan(_:with:)

viewやwindowなどに、1本以上の指が触れたときに呼ばれる

func touchesBegan(Set, with: UIEvent?)

touchesMoved(_:with:)

viewやwindowなどで、1本以上の指が動いたときに呼ばれる

func touchesMoved(Set, with: UIEvent?)

touchesEnded(_:with:)

viewやwindowなどで、1本以上の指が離れたときに呼ばれる

func touchesEnded(Set, with: UIEvent?)

touchesCancelled(_:with:)

システムイベント(システムアラートなど)によりタッチがキャンセルされたときに呼ばれる

func touchesCancelled(Set, with: UIEvent?)

Responder Chainとは

iOSが検知したイベントを処理できるオブジェクトが見つかるまで、

イベントを順次リレーする仕組み。

検知されたイベントはUIApplicationオブジェクトからUIWindowオブジェクトへ渡され、

UIWindowオブジェクトはそのイベントに最も相応しいと思われるオブジェクトにそのイベントをディスパッチする。この時、オブジェクトがイベントを処理できない(=ハンドラがない)場合、

処理可能なオブジェクトが見つかるまで上位のスコープにイベントが渡される。

このようなイベントをリレーするオブジェクトの連鎖がResponderChain。

ResponderChainはイベントにより動的に作成される。

例えばtouchイベントの場合、下記のHitTest(当たり判定)を用いてResponder Chainを形成する。

HitTestの流れ

UIViewが持つhitTest(_ : withEvent)メソッドが、

その内部でpointInside(_ : withEvent)メソッドを呼び

自分のsubViewのうちどれがタッチされたのかを返す

例として下記の図では、

ViewA上にあるViewCをタッチした場合のHitTestを行なっている。

hitTest.png

まとめ

FirstResponderは一番はじめにイベント処理を試みるUIResponderオブジェクトとして

システムに明示的に伝えるもの。

iOSで検知されたイベントはFirstResponderに渡され、

FirstResponderが処理できない場合はResponderChainを使って、次のオブジェクトにイベントがリレーされる。

間違いやご指摘などありましたら、コメントにてご教授いただけますと幸いです。

参考

Apple Developer Documentation

【Swift】Responder Chainの仕組み - Qiita

UIKit&Swiftプログラミング 優れたiPhoneアプリ開発のためのUI実装ガイド

9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?