iOS 14になってからTextField
にフォーカスを当てると適切なポジションに自動でスクロールするようになった。iOS 13ではKeyboardObservingなどのライブラリを利用する必要があったので、標準機能として実装されたのはとても嬉しい。
その一方で、SwiftUIのList上に配置したTextEditor
にフォーカスを当てても自動でスクロールされない。
SwiftUIのTextField
はUIKitでのUITextFieldに相当する1行入力用のViewで、TextEditor
はUITextViewに相当する複数行入力用のViewである。
UITextViewをUIViewRepresentableでラッピングしたView(ここでは仮にCustomTextViewとする)にフォーカスを当てても同様に自動でスクロールされない。CustomTextViewでも uiView.becomeFirstResponder()
を呼んでいるが自動でスクロールしてくれないようだった。
再現環境
- Xcode 13.1
- iPhone X / iOS 14.7
- iPhone 13 Pro / iOS 15.1
再現コード
最小限のコードを作成してみた。
アプリ固有の設定のせいでおかしいのかと思ったが、導入しているライブラリ等を除外しても発生したのでデフォルトで発生する問題のようだ。
import SwiftUI struct ContentView: View { @State private var singleLineText: String = "" @State private var multipleLineText: String = "" var body: some View { List { Section { ForEach(0 ..< 20) { _ in Text("ああああああ") } } Section { TextField( "プレースホルダー", text: $singleLineText ) TextEditor( text: $multipleLineText ) } Section { ForEach(0 ..< 20) { _ in Text("ああああああ") } } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
「プレースホルダー」と表示されたTextFieldにフォーカスを当てると、ソフトキーボードが表示されてListが自動でスクロールされる。
しかしTextEditorにフォーカスを当てても自動でスクロールしてくれない。
isScrollEnabledをfalseに設定しておくと、フォーカスが当たったタイミングで自動でスクロールしてくれる
UITextViewをUIViewRepresentableでラッピングしたViewの場合は、makeUIView(context:)
のタイミングで textField.isScrollEnabled = false
に設定しておくと、フォーカスが当たったタイミングで自動スクロールしてくれることがわかった。
SwiftUI-Introspectを使って、TextEditorの内部に使われているUITextView
を取り出して textView.isScrollEnabled = false
を指定すれば良いと思うが、将来的にメンテナンスしていくアプリでSwiftUI-Introspectは使いたくない。SwiftUI-Introspectを使った場合はこんな感じで対応できると思う。
TextEditor(text: $multipleLineText) .introspectTextView { view in view.isScrollEnabled = false }
TextEditorの中身を取り出す方法はほかにも「[Swift] SwiftUI の TextView から UITextfield を取り出す - Qiita」もあるが、SwiftUI-Introspectと同様の理由が使いたくない。