Search code examples
swiftxcodeswiftuiios14

Switching focus between TextFields in List SwiftUI 2.0


I would like to create a List in SwiftUI with a series of TextFields that the user can switch between by pressing the return key. I found this article, about the FocusBinding property, but I'm not sure I'm on the write path. Originally, I had set up something in Swift 1.0 that wrapped UITextField in UIViewRepresentable, but that was still having some problems. With Swift 2.0, it seems a lot easier to check for changes to a TextField and respond, and it seems like some of the complications of keyboard offsets have been solved for you, so I'd like it if I didn't have to use a wrapper like this.

    struct VirtuesListBody: View {
    @EnvironmentObject var settings: UserSettings
    @State var curIdx = -1

    var body: some View {
        List {
            ForEach(Array(zip(self.settings.virtues.enumerated().filter({ !$1.deleted }).map({ $0.offset }), self.settings.virtues.map({ !$0.deleted }))), id: \.0) { idx, _ in
                VirtuesRowItem(curIdx: self.$curIdx, idx: idx)
            }
        }
    }
}

struct VirtuesRowItem: View {
    @EnvironmentObject var settings: UserSettings
    @Binding var curIdx: Int
    let idx: Int
    
    var body: some View {
        Group {
            if idx == self.settings.virtues.count - 1 {
                TextField("", text: self.$settings.virtues[idx].name, onEditingChanged: { _ in
                    }, onCommit: {
                    })
            } else {
                TextField("", text: self.$settings.virtues[idx].name,
                    onEditingChanged: { _ in
                    }, onCommit: {
                    })
            }
        }
    }
}

Basically, I have a list of VirtuesRowItem Views, and each VirtuesRowItem is constructed of one TextField. If the user is focused on the first TextField in the List, when the return button is pressed, I want the focus to go to the next TextField in the List. If it is the last item, I want the keyboard to be dismissed.


Solution

  • According to the docs on @FocusedBinding

    If multiple views publish bindings using the same key, the wrapped property will reflect the value of the binding from the view closest to focus.

    It doesn’t seem to allow you to be able to manipulate the firstReponder.

    So unfortunately, you’ll have to stick with a wrapped UITextField.