5

Say we have the following view of two text fields:

struct ContentView: View {
    
    @State private var first = ""
    @State private var second = ""
    
    var body: some View {
        VStack {
            TextField("First", text: $first)
                .toolbar {
                    ToolbarItem(placement: .keyboard) {
                        Button("Test") { }
                    }
                }
            
            TextField("Second", text: $second)
        }
    }
}

The toolbar modifier is applied only to the "first" text field. My expectation is therefore that it only shows up on the keyboard, when the "first" text field is in focus. What happens in practice though, it that it also shows up when the "second" text field is in focus.

Is this intended behaviour? And if so, how can I have different keyboard toolbars for different text fields?

Marcus Rossel
  • 3,196
  • 1
  • 26
  • 41
  • 1
    Yes, it is intended behavior - it does not matter to which view you attach toolbar to present it. If you need to attach it conditionally then look for `FocusState` and `focused` and how to make conditional modifiers. – Asperi Oct 07 '21 at 09:38
  • The only way to specify the toolbar for a specific textfield is to go back to UIKit. I don't particularly like this setup either. When scanning text it can get pretty odd with this feature. – lorem ipsum Oct 07 '21 at 11:42
  • `FocusState` and `focused` make no difference here, adding a toolbar to apparently one text field adds it to all of them regardless of focus. – Jimbo Dec 28 '21 at 18:57

2 Answers2

2

The only thing that I've found so far that solves this problem works, but doesn't feel right. It also generates some layout constraint warnings in the console.

If you wrap each TextField in a NavigationView each `TextField will have its own context and thus its own toolbar.

Something like this:

struct ContentView: View {
    
    @State private var first = ""
    @State private var second = ""
    
    var body: some View {
        VStack {
          NavigationView {
            TextField("First", text: $first)
                .toolbar {
                    ToolbarItem(placement: .keyboard) {
                        Button("Test") { }
                    }
                }
          }
          NavigationView {  
            TextField("Second", text: $second)
          }
        }
    }
}
sidekickr
  • 384
  • 3
  • 8
0

You can define a selection variable that gets set when your TextField is selected.

Then, in your .toolbar, you can check for what TextField is being edited and then display your appropriate Button.

Wrapping it in a NavigationView does work, but it also causes layout issues. This solution will also work in a ForEach loop.

struct ContentView: View {
    
    @State private var first = ""
    @State private var second = ""
    @State private var selection: Int?
    
    var body: some View {
        VStack {
            TextField("First", text: $first, onEditingChanged: { isEditing in
                self.selection = isEditing ? 1 : nil
            })
            
            TextField("Second", text: $second, onEditingChanged: { isEditing in
                self.selection = isEditing ? 2 : nil
            })
            
        }
        .toolbar {
            if selection == 1 {
                ToolbarItemGroup(placement: .keyboard) {
                    Button("Test") { }
                }
            }
        }
    }
}
griv
  • 2,098
  • 2
  • 12
  • 15