soranoba
soranoba Author of soranoba.net
programming

[ReactNative] subviewからタッチイベントを奪う

ネイティブアプリにおいて, ちょっと面倒だけどよく使うものの1つにsubviewのタッチイベントを無効化して, superviewがタッチイベントを処理したいケースというのがある.
iOSでいうところのUIView # hitTest:withEvent:で行う処理である.

ReactNativeの場合にどうするか, 割と分かりにくいところに書いてあったので備忘録的に書いておこうと思う.

ReactNativeでは最も深い位置のsubviewから順にresponderの判定が行われる

そもそもReactNativeでTouchableを扱おうとする場合, TouchableWithoutFeedbackを用いた派生クラスを用いる. 派生クラスと書いているが, 基本的には継承関係になく, これらを子に持つComponentである.

これらがResponderになる為のフローはGesture Responder Systemにおいて記載がある.
onStartShouldSetResponderの返り値によって, subviewから順番にresponderになるターゲットを判断するようだ. コードでいうと, ここらへんにある.

ところで, responderの判定の仕方がiOSとは全く異なるというのが面白い. iOSはrootから順番にUIView # hitTest:withEvent:を呼び出していき, superviewからsubviewへと判断される.
一方で, ReactNativeはsubviewからsuperviewへと判定されていくのがデフォルトの挙動である.

onStartShouldSetResponder and onMoveShouldSetResponder are called with a bubbling pattern, where the deepest node is called first.

Gesture Responder Systemより

SuperviewがResponderになる方法

disabledを使う方法

先にhack的な方法を紹介する. ドキュメントに辿り着くまでにググって出てきた方法はこちらだった.

<Link to="/"><Button disabled style={style} /></Link>

disabledを設定することでここでonStartShouldSetResponderがfalseになることを使う方法である. しかし, この方法だとdisabled用のstyleが適用されてしまう. そこで, styleを自分で指定することでそれっぽい見た目にする.

onStartShouldSetResponderCaptureを使う方法

<Link to="/" onStartShouldSetResponderCapture={(evt) => true} ><Button /></Link>

Gesture Responder Systemに記載されている, Superviewがresponderになる方法を使う.
これで設定したComponentの子要素がresponderにならずに, 設定したComponent自身がresponderになることができる.
但し, 一般的なTouchableWithoutFeedbackなどではこの値が子要素に伝搬されていない為, 使うことができなかったりする.

(Updated: )

comments powered by Disqus