SwiftUI Keyboard Toolbar Scope
Asked Answered
C

2

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?

Cerated answered 7/10, 2021 at 9:30 Comment(3)
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.Stopover
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.Monophony
FocusState and focused make no difference here, adding a toolbar to apparently one text field adds it to all of them regardless of focus.Ensoul
A
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)
          }
        }
    }
}
Anglice answered 9/3, 2022 at 3:32 Comment(0)
T
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") { }
                }
            }
        }
    }
}
Thyroid answered 3/3, 2023 at 2:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.