SwiftUIでテキストの連続入力を実装したいと思ったが、iOSで一瞬ソフトウェアキーボードが閉じてしまう問題があった。そのあたりの解消の工夫をしたのでブログに書いておく。
やりたかったことと課題
以下のように、入力 => done => 入力 => doneと繰り返して、連続入力させたかった。
しかしSwiftUIのTextField + forcused + onSubmitだと、doneを押した時に一瞬だけソフトウェアキーボードが閉じ、また開くという挙動をする。めちゃくちゃ体験が悪い。
初期のシンプルなonSubmitの実装
struct ContentView: View { @State private var inputText = "" @State private var items: [String] = [] @FocusState private var isInputFocused: Bool var body: some View { VStack { ForEach(items, id: \.self) { item in Text(item) } TextField("新しいアイテムを追加", text: $inputText) .focused($isInputFocused) .onSubmit { addItem() isInputFocused = true } } } private func addItem() { let trimmedText = inputText.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmedText.isEmpty else { return } items.append(trimmedText) inputText = "" } }
解決策
この解決にはいろいろなやり方がありそうだが、今回は複数行入力できるTextField + onChangeで実装した。ただし、onChangeのみ使うとmacOSではEnterキーが効かないという問題が別で起こってしまったので、プラットフォーム別に分岐して実装した。
import SwiftUI struct ContentView: View { @State private var inputText = "" @State private var items: [String] = [] @FocusState private var isInputFocused: Bool var body: some View { VStack { ForEach(items, id: \.self) { item in Text(item) } // axis: .verticalで複数行入力に対応 TextField("新しいアイテムを追加", text: $inputText, axis: .vertical) // 追加時にisInputFocusedの状態を変えないので、入力し続けられる .focused($isInputFocused) #if os(iOS) // iOS: onChangeで改行文字を検知して追加処理を実行 // onSubmitを使うとキーボードが一瞬閉じてしまう .onChange(of: inputText) { _, newValue in guard isInputFocused else { return } // 改行文字(\n)が含まれているなら追加処理を実行 guard newValue.contains("\n") else { return } addItem() } #else // macOSなどiOS以外: 通常通りonSubmitでEnterキー押下を検知 // macOSでonChangeを使うとEnterキーで追加できなくなる .onSubmit { addItem() } #endif } } private func addItem() { let trimmedText = inputText.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmedText.isEmpty else { return } items.append(trimmedText) inputText = "" } }
まとめ
上記実装により、最初やりたかったことは解決し、iOSでもmacOSでも両方良い感じに連続入力できるようになった。
一方、iOSでハードウェアキーボードがある時はどうするの?という問題も別である。本当にちゃんとやりたいなら以下のようなやり方もありそうだ。
- ソフトウェアキーボードが開いているかを判断して切り替え
- UIKitでうまいこと実装してSwiftUIに統合する