Extend SwiftUI Keyboard with Custom Button
Solved the issue using UIRepresentable protocol
struct TestTextfield: UIViewRepresentable { @Binding var text: String var keyType: UIKeyboardType func makeUIView(context: Context) -> UITextField { let textfield = UITextField() textfield.keyboardType = keyType let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: textfield.frame.size.width, height: 44)) let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(textfield.doneButtonTapped(button:))) toolBar.items = [doneButton] toolBar.setItems([doneButton], animated: true) textfield.inputAccessoryView = toolBar return textfield } func updateUIView(_ uiView: UITextField, context: Context) { uiView.text = text }}extension UITextField{ @objc func doneButtonTapped(button:UIBarButtonItem) -> Void { self.resignFirstResponder() }}
Using in content view
struct ContentView : View { @State var text = "" var body: some View { TestTextfield(text: $text, keyType: UIKeyboardType.phonePad) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 50) .overlay( RoundedRectangle(cornerRadius: 16) .stroke(Color.blue, lineWidth: 4) ) }}
I did it in 5 lines using the SwiftUI-Introspect library! I had a problem that the Representable Text Field did not react in any way to padding and frame. Everything was decided using this library!
Link: https://github.com/siteline/SwiftUI-Introspect
import SwiftUIimport Introspectstruct ContentView : View {@State var text = ""var body: some View { TextField("placeHolder", text: $text) .keyboardType(.default) .introspectTextField { (textField) in let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: textField.frame.size.width, height: 44)) let flexButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil) let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(textField.doneButtonTapped(button:))) doneButton.tintColor = .systemPink toolBar.items = [flexButton, doneButton] toolBar.setItems([flexButton, doneButton], animated: true) textField.inputAccessoryView = toolBar }}extension UITextField { @objc func doneButtonTapped(button:UIBarButtonItem) -> Void { self.resignFirstResponder() }}
If there is an issue with @Binding
implement the Coordinator class which conforms to the UITextFieldDelegate. This will allow one to be able to customize the TextField further if required.
struct DecimalTextField: UIViewRepresentable { private var placeholder: String @Binding var text: String init(_ placeholder: String, text: Binding<String>) { self.placeholder = placeholder self._text = text } func makeUIView(context: Context) -> UITextField { let textfield = UITextField() textfield.keyboardType = .decimalPad textfield.delegate = context.coordinator textfield.placeholder = placeholder let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: textfield.frame.size.width, height: 44)) let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(textfield.doneButtonTapped(button:))) toolBar.items = [doneButton] toolBar.setItems([doneButton], animated: true) textfield.inputAccessoryView = toolBar return textfield } func updateUIView(_ uiView: UITextField, context: Context) { uiView.text = text } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextFieldDelegate { var parent: DecimalTextField init(_ textField: DecimalTextField) { self.parent = textField } func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { if let currentValue = textField.text as NSString? { let proposedValue = currentValue.replacingCharacters(in: range, with: string) as String self.parent.text = proposedValue } return true } } }