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の表示位置がズレて困ったけど、なんとか対処してみた、という話でした。
もっといい方法もあるのかもしれませんが、もしかしたら誰かの役に立つかもしれないし、未来の自分もたぶんまた忘れるので、そのときのためのメモとして残しておくことにします。
おしまい