Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI - Adding a keyboard toolbar button for only one TextField adds it for all TextFields

Background

I have two TextFields, one of which has a keyboard type of .decimalPad.

Given that there is no 'Done' button when using a decimal pad keyboard to close it, rather like the return key of the standard keyboard, I would like to add a 'Done' button within a toolbar above they keypad only for the decimal keyboard in SwiftUI.

Problem

Adding a .toolbar to any TextField for some reason adds it to all of the TextFields instead! I have tried conditional modifiers, using focussed states and checking for the Field value (but for some reason it is not set when checking, maybe an ordering thing?) and it still adds the toolbar above the keyboard for both TextFields.

How can I only have a .toolbar for my single TextField that accepts digits, and not for the other TextField that accepts a string?

Code

Please note that I've tried to make a minimal example that you can just copy and paste into Xcode and run it for yourself. With Xcode 13.2 there are some issues with displaying a keyboard for TextFields for me, especially within a sheet, so maybe simulator is required to run it properly and bring up the keyboard with cmd+K.

import SwiftUI

struct TestKeyboard: View {
    @State var str: String = ""
    @State var num: Float = 1.2

    @FocusState private var focusedField: Field?
    private enum Field: Int, CaseIterable {
        case amount
        case str
    }

    var body: some View {
        VStack {
            Spacer()
            
            // I'm not adding .toolbar here...
            TextField("A text field here", text: $str)
                .focused($focusedField, equals: .str)

            // I'm only adding .toolbar here, but it still shows for the one above..
            TextField("", value: $num, formatter: FloatNumberFormatter())
                .keyboardType(.decimalPad)
                .focused($focusedField, equals: .amount)
                .toolbar {
                    ToolbarItem(placement: .keyboard) {
                        Button("Done") {
                            focusedField = nil
                        }
                    }
                }

            Spacer()
        }
    }
}

class FloatNumberFormatter: NumberFormatter {
    override init() {
        super.init()
        
        self.numberStyle = .currency        
        self.currencySymbol = "€"
        self.minimumFractionDigits = 2
        self.maximumFractionDigits = 2
        self.locale = Locale.current
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
}

// So you can preview it quickly
struct TestKeyboard_Previews: PreviewProvider {
    static var previews: some View {
        TestKeyboard()
    }
}

like image 574
Jimbo Avatar asked Sep 10 '25 20:09

Jimbo


1 Answers

Try to make toolbar content conditional and move toolbar outside, like below. (No possibility to test now - just idea)

Note: test on real device

var body: some View {
    VStack {
        Spacer()
        
        TextField("A text field here", text: $str)
            .focused($focusedField, equals: .str)

        TextField("", value: $num, formatter: FloatNumberFormatter())
            .focused($focusedField, equals: .amount)
            .keyboardType(.decimalPad)

        Spacer()
    }
    .toolbar {          // << here !!
        ToolbarItem(placement: .keyboard) {
            if field == .amount {             // << here !!
               Button("Done") {
                  focusedField = nil
               }
            }
        }
    }

}
like image 104
Asperi Avatar answered Sep 12 '25 19:09

Asperi