SwiftUI: iOS 17でTextFieldのクリアができなくなっている問題
iOS 16でうまく動く以下のコードが、iOS 17になってから機能しなくなっています。
(iOS 17.1.2の実機、iOS 17.2のシミュレータで確認。iOS 16は16.7.2の実機で確認)
struct TextFieldWithClearButton: View {
var title: LocalizedStringKey
@Binding var text: String
var prompt: LocalizedStringKey
var body: some View {
HStack {
TextField(title, text: $text, prompt: Text(prompt))
.textFieldStyle(.roundedBorder)
.padding(.horizontal, 10)
Button(action: {
text = ""
}, label: {
Image(systemName: "xmark.circle.fill")
})
.buttonStyle(.borderless)
.foregroundColor(.gray)
.padding(.trailing, 10)
}
}
}
やっていることは、
-
TextField
の横に×ボタンをつける - ×ボタンがタップされたら、
TextField
のtext:
の変数をクリアする
というだけなんですが、iOS 17だと、日本語入力後に×ボタンをタップしてクリアしても、TextField
の描画が更新されません。変数自体はクリアされているので、変数の内容を元に別のビューの更新がかかる場合、そっちは更新されます。
また、入力された日本語じゃない場合(アルファベットや数字、記号)はクリア(表示更新)されます。
もうちょっと言うと、IMが起動されてしまうと半角数字に変換して入力したとしても表示が更新されなくなります。
IM起動後でも、一回バックスペースを打つと、クリアできるようになります。
×ボタンはクリアする機能なんですが、それ以外のボタンなどで外からTextField
のtext:
変数を上書きするような機能は全面的にダメな(=IM起動直後では表示が更新されなくなる)ように見えます。
困りました。
Button
のaction:
を以下のようにすると、ひとまずはうまく行くように見えます。
Button(action: {
if !text.isEmpty {
let _ = text.removeLast()
Task { @MainActor in
self.text = ""
}
}
}, label: {
Image(systemName: "xmark.circle.fill")
})
1文字削ってから(バックスペースを打つののシミュレーション)、Task
でtext:
変数を更新します。
- 上記は1文字削ってますが、半角スペースを足すとかでも良さそうです。ただ、何かしら
text:
変数を操作してからでないとうまく機能しないようです。 -
Task
を使って非同期実行していますが、Task
を使わずに直接クリアしてもダメなようです。 - 1文字削る処理も
Task
に入れると、それはそれでダメなようです。1文字削るのと、クリアするのは別にする必要があるようです。
問題は、一回変数操作を挟んでいるので、この変数に依存して描画が更新されるビューは2回再描画が走るということですね。
影響がどれぐらいあるのか次第でしょうか。
IMを使う場合にだけ影響が出るように見えますので、英語圏とかからはAppleにバグ報告されない気もしますね。
追記)フィードバックアシスタントで報告しました。
上記は1文字削ってますが、半角スペースを足すとかでも良さそうです
削る方はダメですね。1文字だけ入力されているケースでは、削ったのが空文字列になってしまって(=Task
での更新結果と同じになってしまって)うまく動作しません。
足す方がよさそう。
追記)
自分で参照する際に、間違ってダメな方をコピペしそうになったので、改めてよさげな方を載せておきます。
Button(action: {
if !self.text.isEmpty {
self.text = self.text + " "
Task { @MainActor in
self.text = ""
}
}
}, label: {
Image(systemName: "xmark.circle.fill")
})
Xcode 16.0(16A242d)のiOS18 シミュレータやプレビューはダメですが、実機のiOS 18.0製品版はうまく動いている(回避策をしなくてもクリアされる)ように見えます。
なんでしょうね?