経緯
とあるアプリの開発で、ユーザのタップ操作に即座に反応する必要があり、画面の左右にボタン領域を用意して、 onTapDown
でイベントをハンドリングしようとしました。
iOS simulatorで確認しているところでは上手く動いていたのですが、iPhone XRなどの実機で確認してもらうと、「画面を押した瞬間に反応せず、コンマ数秒経ってから反応しているように見える」という指摘があり、調査しました。
状況としては、以下の動画のようなイメージです。
最初の2タップは、比較的中央寄りをタップしているので、指が触れた瞬間ぐらいでテキストの行が増えています。
次の2タップは、意図的に画面端をタップしており、タップした瞬間から500msecぐらい遅れて行が増えています。
調査していくと、iOSのOS側の制御として「エッジに触れられたときには、OSのジェスチャーかもしれないから、アプリにイベントを渡すのは少し待つ」的な動作がありそうでした。
そして、iOSの preferredScreenEdgesDeferringSystemGestures
にて [.left, .right]
を返せば良さそう、、、という記事も発見したのですが、今回の開発ではFlutterを利用していたため、UIViewControllerが露出しておらず、継承してそれを利用する方法がわかりませんでした。
解決策
調べていくと、Flutterで利用されている UIViewController は、 FlutterViewController
ということがわかりました。
また、同僚からのアドバイスで、 extension
を使ってみました。
結果としては、 {project dir}/ios/Runner/AppDelegate.swift
の末尾に、以下の記述を追加することで改善しました。
public extension FlutterViewController {
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
return [.left, .right]
}
}
改善後の動画です。
こちらも、上の動画と同様に、最初の2タップは中央寄り、次の2タップは端をタップしていますが、どちらもタップした瞬間に反応しています。
その他
同じように、 preferredScreenEdgesDeferringSystemGestures
にて [.bottom]
を返すと、1度はホーム画面への遷移を防げます。
一般的なアプリで実装されているとただ迷惑ですが、ガチャガチャ操作するようなゲームなどでは、この指定をしても良いかもしれませんね。
その他2
多くのアプリでは、こういった制御が必要になったとしても、特定の画面のみかと思います。
Flutterパッケージを作成し、 preferredScreenEdgesDeferringSystemGestures
が返す値を変数化、Flutterアプリ側からの呼び出しでその変数を書き換え、 setNeedsUpdateOfScreenEdgesDeferringSystemGestures
を呼び出してやる、、、というのが良さそうではあります。