6

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?

3
  • 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. Commented Oct 7, 2021 at 9: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. Commented Oct 7, 2021 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. Commented Dec 28, 2021 at 18:57

3 Answers 3

1

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)
          }
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

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") { }
                }
            }
        }
    }
}

Note: This seems to only work on iOS 16+

2 Comments

Same with @Artem's answer. This seems to work on iOS 16+ and the question has the iOS 15 flag.
I missed that, I will update my answer to represent this -- thanks. I would suggest upgrading your apps target to 16 then and use the #available(iOS 16.0 *) attribute where applicable.
0

I'm a very new to SwiftUI, but you can use the isFocused flag as a condition for adding item to the toolbar for each text field.

@FocusState private var isFirstFieldFocused: Bool
@State private var firstFieldValue: String = ""
 
var body: some View {
  TextField("First label", text: $firstFieldValue)
    .focused($isFirstFieldFocused)
    .toolbar {
      if isFirstFieldFocused {
        ToolbarItemGroup(placement: .keyboard) {...}
      }
    }
}

1 Comment

This seems to be available only for iOS 16+. "Conformance of 'Optional<Wrapped>' to 'ToolbarContent' is only available in iOS 16.0 or newer"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.