HC77
@HC77

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Swift @FocusStateが子ビューでうまく機能しない

解決したいこと

子ビューでフォーカスを制御したいのですが、うまくいきません。
二つのテキストフィールドのうち、一つ目のテキストフィールドの入力が終わりユーザーが改行を押した時に
二つ目のテキストフィールドに自動的にフォーカスされるようにしたいです。
具体的には、focusedモディファイアのところに以下のようなエラーメッセージが出ます。

発生している問題・エラー

Accessing FocusState's value outside of the body of a View. This will result in a constant Binding of the initial value and will not update.

該当するソースコード(親ビュー)

struct ContentView: View {

    @State  var selectedTab: Int = 0


    var body: some View {

        TabView(selection: $selectedTab) {

            HomeTabView()
                .tabItem({
                    Image(systemName: "house")
                    Text("Home")
                })

            ReminderTabView()
                .tabItem {
                    Image(systemName: "checklist")
                    Text("Reminder")
                }.tag(1)

            SettingTabView()
                .tabItem({
                    Image(systemName: "gear")
                    Text("Setting")
                }).tag(2)
        }

    }
}

該当するソースコード(子ビュー)

struct HomeTabView: View {

    @StateObject var textField_What = MyTextField()
    @StateObject var textField_When = MyTextField()


    var body: some View {

        VStack(alignment: .leading) {

            Text("何を?").padding(.leading)

            TextField("テキストフィールド",
                      text: $textField_What.content,
                      onEditingChanged: { begin in
                if begin {
                    self.textField_What.isEditting = true
                } else {
                    self.textField_What.isEditting = false
                }
            },
                      onCommit: {
                textField_When.isFocused = true
            })
            .focused(textField_What.$isFocused)  // エラーが表示されます。
            .textFieldStyle(.roundedBorder)
            .padding(.horizontal)
            .shadow(color: textField_What.isEditting ? .blue : .clear, radius: 3)


            Text("いつ?").padding([.leading, .top])

            TextField("テキストフィールド",
                      text: $textField_When.content,
                      onEditingChanged: { begin in
                if begin {
                    self.textField_When.isEditting = true
                } else {
                    self.textField_When.isEditting = false
                }
            })
            .focused(textField_When.$isFocused)  // エラーが表示されます。
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .padding(.horizontal)
            .shadow(color: textField_When.isEditting ? .blue : .clear, radius: 3)

        }
    }
}


class MyTextField: ObservableObject {

    @Published var content = ""
    @Published var isEditting = false
    @FocusState var isFocused

}

構成自体は間違っていないと思うので、必要な部分だけを別のプロジェクトで作ってみたのですが、そちらはうまくいきました。

上手くいったソースコード(親ビュー)

struct ContentView: View {
    var body: some View {
        SubView()
    }
}

上手くいったソースコード(子ビュー)

struct SubView: View {

    @State private var string1 = ""
    @State private var isEditting1 = false
    @FocusState var isFocused1
    @State private var string2 = ""
    @State private var isEditting2 = false
    @FocusState var isFocused2

    var body: some View {

        VStack {
            Text("改行で次へ").bold().font(.title2)

            TextField("aaa", text: $string1,
                      onCommit: {
                isFocused2 = true
            })
            .padding()
            .textFieldStyle(.roundedBorder)
            .focused($isFocused1)

            TextField("bbb", text: $string2)
                .padding()
                .textFieldStyle(.roundedBorder)
                .focused($isFocused2)
        }
    }
}

やっていることは、先ほどのものと変わらず、テキストフィールドで値が送信されたときに@FocuesStateがついたプロパティを切り替えています。

0

2Answer

原因

@FocusStateObservableObjectで持っていることがうまくいかない原因だと思います。

その他問題

問題1

MyTextField()を変数名を変えて2個持つというのは良くないと思います。

    @StateObject var textField_What = MyTextField()
    @StateObject var textField_When = MyTextField()

問題2

このテキストフィールドはiOS14から非推奨になりました

TextField(String, text: Binding<String>, onEditingChanged: (Bool) -> Void, onCommit: () -> Void)

ここの記事が参考になると思います。

完成形

struct HomeTabView: View {
    
    @StateObject var textField_What = MyTextField()
    
    @FocusState var isFocused: FocusTextFieldName?
    
    enum FocusTextFieldName: Hashable {
        case firstTextField
        case secondTextField
    }
    
    var body: some View {
        VStack(alignment: .leading) {
            Text("何を?").padding(.leading)
            
            TextField("FirstTextField", text: $textField_What.firstTextFieldText)
                .focused($isFocused, equals: .firstTextField)
                .textFieldStyle(.roundedBorder)
                .padding(.horizontal)
                .shadow(color: isFocused == .firstTextField ? .blue : .clear, radius: 3)
                .onSubmit {
                    isFocused = .secondTextField
                }
            
            Text("いつ?").padding([.leading, .top])
            
            TextField("SecondTextField", text: $textField_What.secondTextFieldText)
                .focused($isFocused, equals: .secondTextField)
                .textFieldStyle(.roundedBorder)
                .padding(.horizontal)
                .shadow(color: isFocused == .secondTextField ? .blue : .clear, radius: 3)
        }
    }
}


class MyTextField: ObservableObject {
    @Published var firstTextFieldText = ""
    @Published var secondTextFieldText = ""
}

GIF

Simulator Screen Recording - iPhone 12 - 2022-09-10 at 01.34.05.gif

0Like

すいません、回答が来ていることに今気がつきました。
一年以上前になりますが、回答ありがとうございました!

0Like

Your answer might help someone💌