SwiftUIでTextEditorにApple Pencilで手書き入力(Scribble)を使うとTextEditorの位置がズレる問題をなんとかした、の巻

SwiftUIでTextEditorにApple Pencilで手書き入力(Scribble)を使うとTextEditorの位置がズレる問題をなんとかした、の巻

はじめに

最近、iOSアプリを作っていまして。
SwiftUIでTextEditorにてテキスト入力ができる機能をもつアプリを作っています。

TextEditorは画面の下部に配置しています。

んで、iPadでTextFieldやTextEditorを使うと、Apple PencilのScribbleでそのまま入力できるのですが、

それを試していたときに、ちょっとおかしな挙動に遭遇しました。

Apple Pencilで手書き入力を開始すると、TextEditorの位置が動いてしまって、Apple Pencilで入力開始した位置とズレる。

指でタップしてソフトウェアキーボードを出す場合はTextEditorはきちんとせり上がって位置が調整され問題はない。

気持ち悪いので、原因を探りつつ対処してみた話です。

起きていたこと

現象としてはこんな感じです。

  • SwiftUIで作った、画面下部に配置したTextField / TextEditor
  • Apple Pencilで手書き入力を開始
  • Scribble用のパレットが表示される
  • すると
    View全体のレイアウトが微妙に変わる
  • で、
    TextField / TextEditorの表示位置が入力位置からズレる
  • 一通りズレて再描画が完了したら、ズレずに入力できるようになる

という状態です。こんな感じ。
ちょっと見にくいですが、TextEditorの枠が入力開始位置からずれています。

原因っぽいやつ

内部の仕様を完全に追えているわけではないですが、挙動から考えるとこうなっています。

Scribbleの場合も、Scribbleのパレットが表示される時に、キーボード表示のときと同様にsafeAreaが変化して、TextEditorの位置を調整する

しかし、

Apple Pencilで入力を開始したタイミングでレイアウトが変わってしまうため、結果的に書いている最中に、ペンの先を置いている位置とTextEditorの表示位置にズレが生じる

ハマりポイント

最初は素直にこういう対応を試しました。

.ignoresSafeArea(.keyboard)

これは効いて、Scribble用のパレットが表示されても、TextEditorの位置が固定されるようになりました。

ただし問題があって、

指でタップしてソフトウェアキーボードを表示した場合、
TextEditorのViewが押し上げされなくなり、キーボードの下に埋もれてしまうようになる

どちらかを立てると、どちらかが崩れる っていう、なんとも歯痒い展開になってしまったので、色々試すことになりました。

解決方法

で、最終的に落ち着いたのがこの形

let isPad = UIDevice.current.userInterfaceIdiom == .pad

GeometryReader { geometry in
    ZStack {
        TextEditor
    }
    .padding(.bottom, isPad ? (geometry.safeAreaInsets.bottom < 100 ? 60 : geometry.safeAreaInsets.bottom) : 0)
    .ignoresSafeArea(.keyboard, edges: isPad ? .bottom : [])
}

コードの説明

① safeAreaを自前で制御

geometry.safeAreaInsets.bottom < 100 ? 60 : geometry.safeAreaInsets.bottom

  • Apple Pencil用のパレットと、キーボードでSafeAreaの大きさを切り分ける。
  • 100という数字はiPadでのキーボードの高さが300px程度で、Apple Pencilのパレットは60px程度なので、ざっくりした閾値として使っています。

② iPad限定にする

isPad

  • Scribbleを使うiPadのみで適用

👉 不要な影響を防ぐ

③ keyboard無視は併用する

.ignoresSafeArea(.keyboard)

  • SafeAreaの自動調整を無視して自前で制御

結果

  • キーボード入力 → 問題なし
  • Apple Pencil(Scribble) → 問題なし
  • 入力位置のズレ → 解消

TextEditorの枠がずれずにそのまま入力できています。見づらくてすみません。。。

まとめ

今回の挙動をまとめると、

  • SwiftUIで作った、画面下部に配置したTextField / TextEditor
  • Apple Pencilで手書き入力(Scribble)しようとすると、パレットが表示されるためにTextField / TextEditorの位置が修正される
  • 結果として、Apple Pencilのペン先が接地している位置と、TextField / TextEditorがズレる

、と。

対策としては、

safeAreaを自前で補正する

という形になりました。

おわりに

今回は、ScribbleでTextEditorを使ったらTextEditorの表示位置がズレて困ったけど、なんとか対処してみた、という話でした。


もっといい方法もあるのかもしれませんが、もしかしたら誰かの役に立つかもしれないし、未来の自分もたぶんまた忘れるので、そのときのためのメモとして残しておくことにします。

おしまい