SwiftUIで、Listに配置したTextFieldで日本語入力するとカーソルが文末へ勝手に移動する

環境

TextFieldの日本語入力を確定するとカーソルが勝手に文末へ移動する

Mac向けのアプリで、Listの行としてTextFieldを配置するようなUIをSwiftUIで実装しました。
そして、動作確認でTextFieldに日本語を入力してエンターキーで確定させたところ、カーソルが文末へ勝手に移動する現象が起きました。

文末へ勝手に移動する様子がこちら。

入力確定後にカーソルが勝手に移動する

実装は以下の通り。

struct ContentView: View {
    
    @State private var editable1: String = "日本語を[]してもカーソルは動かない"
    @State private var editable2: String = "日本語を[]するとカーソルが最後尾に移動する"
    
    var body: some View {
        VStack(alignment: .leading) {
            Text("ここはリストの外")
            TextField("something", text: $editable1)
        }

        List {
            VStack(alignment: .leading) {
                Text("ここはリストの中")
                TextField("something", text: $editable2)
            }
            .background(Color(.cyan))
        }
    }
}

日本語入力を確定させるたびにカーソルが文末へ移動するのは、さすがに困りました。というより、なぜただのTextFieldなのに移動してしまうのでしょう?
ちなみに、半角英数字だとエンターキーを押さないので、当然ですが、カーソルが文末へ移動することはありません。

OvservableObjectを使う、またはサブビューを使うと回避できた

いろいろ試した結果、2つの回避方法が見つかりました。

  1. OvservableObjectを使う
  2. サブビューを作り、それを使う

ちなみに、サブビューにOvservableObjectを渡す方法でも問題ありませんでした。

検討した実装は以下の通り。

struct ContentView: View {
    
    @State private var editable1: String = "日本語を[]してもカーソルは動かない"
    @State private var editable2: String = "日本語を[]するとカーソルが最後尾に移動する"
    
    @ObservedObject private var editable3 = ObservableEditable()
//    @ObservedObject private var editable4 = ObservableEditable()

    var body: some View {
        VStack(alignment: .leading) {
            Text("ここはリストの外")
            TextField("something", text: $editable1)
        }

        List {
            VStack(alignment: .leading) {
                Text("ここはリストの中")
                TextField("something", text: $editable2)
            }
            .background(Color(.cyan))
        }

        // 回避できる
        List {
            VStack(alignment: .leading) {
                Text("ここはリストの中でObservableObjectを使う")
                TextField("something", text: $editable3.txt)
                Text("表示も[\(editable3.txt)]に変わる")
            }
            .background(Color(.yellow))
        }

        // これも回避できる
        List {
            VStack(alignment: .leading) {
                Text("ここはリストの中のサブビューを使う")
                SubTextField()
            }
            .background(Color(.green))
        }

// ObservalObjectをサブビューに渡しても回避できる
//        List {
//            VStack(alignment: .leading) {
//                Text("ここはリストの中のサブビューを使う")
//                SubTextField(content: editable4)
//            }
//            .background(Color(.green))
//        }

    }
}

// ObservableObjectを使う
class ObservableEditable: ObservableObject {
    @Published var txt: String = "日本語を[]してもカーソルは動かない"
}

// サブビューを使う
struct SubTextField: View {
    @State private var txt: String = "日本語を[]してもカーソルは動かない"
    
    var body: some View {
        TextField("something", text: $txt)
    }
}

// サブビューにObservableObjectを渡して使う
//struct SubTextField: View {
//    @ObservedObject var content: ObservableEditable
//
//    var body: some View {
//        TextField("something", text: $content.txt)
//    }
//}

回避後の動きがこちら。

カーソルが勝手に移動するのを回避できた

カーソルが文末へ勝手に移動する理由は不明

正直なところ、理由は分かりませんでした。
もしかしたら、私がTextFieldのドキュメントで何か見落としているのかもしれませんし、TextField自体のバグかもしれません。
実際、TextFieldの入力時に以下のエラーが出力されるのですが、これを調べてみるとXcode14からのバグだそうです。

This method should not be called on the main thread as it may lead to UI unresponsiveness.

StackOverflowの投稿より

stackoverflow.com

このバグが改善されたときに、どうなるかですね。