|
hi everybody!
does anyone know an easy and elegant way of dismissing the keyboard when finished with entry (especially when using numberPad or decimalPad) from a textField in SwiftUI? Also to add an clearButton and once the entry is cleared, that the placeholder will be shown?
Thanks a lot in advance for helping me out!
Rgds, Martin
|
|
Hi Martin
Hope this helps. Not sure the best way but found it works for me
struct ContentView: View {
@State private var someNumber = ""
@State private var enteredNumber = ""
var body: some View {
VStack {
Text("\(enteredNumber)")
TextField("PlaceHolder", text: $someNumber)
.keyboardType(.decimalPad)
Button("Submit") {
self.enteredNumber = self.someNumber
self.someNumber = "" // Clear text
UIApplication.shared.endEditing() // Call to dismiss keyboard
}
}
.padding()
}
}
// extension for keyboard to dismiss
extension UIApplication {
func endEditing() {
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
if you want the view to move up and down to make room for keyboard you will need to add a func with
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { key in
let value = key.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
self.value = value.height
}
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { key in
self.value = 0
}
and add .onAppear(perform: ----) to the view
|
|
Thanks a lot Nigel!
I had to chance my idea of creating my UI, but your way makes it really easy to use now. thanks a lot!
Rgds, Martin
|
|
struct CustomTextfield: UIViewRepresentable {
@Binding var text: String
var keyType: UIKeyboardType
var placeHolder: String
func makeUIView(context: Context) -> UITextField {
let textfield = UITextField()
textfield.keyboardType = keyType
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
}
CustomTextfield(text: self.$updateAltEvent.rawScoreMin, keyType: UIKeyboardType.numberPad, placeHolder: "Minutes")
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 50)
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(Color(red: 0.7, green: 0.7, blue: 0.7), lineWidth: 1)
)
CustomTextfield(text: self.$updateAltEvent.rawScoreSec, keyType: UIKeyboardType.numberPad, placeHolder: "Seconds")
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 50)
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(Color(red: 0.7, green: 0.7, blue: 0.7), lineWidth: 1)
)
}
extension UITextField{
@objc func doneButtonTapped(button:UIBarButtonItem) -> Void {
self.resignFirstResponder()
}
}
> Implement here
CustomTextfield(text: self.$updateAltEvent.rawScoreSec, keyType: UIKeyboardType.numberPad, placeHolder: "Seconds")
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 50)
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(Color(red: 0.7, green: 0.7, blue: 0.7), lineWidth: 1)
)
|
|
On a same idea I make custum deciamal textfield :
//
// Extention UITextField+UIApplication End Editing Button.swift
// Time Is Money
//
// Created by Sebastien REMY on 15/07/2020.
// Copyright © 2020 MonkeyDev. All rights reserved.
//
import SwiftUI
// Usage
//var currencyFormatter: NumberFormatter {
// let formatter = NumberFormatter()
// formatter.locale = .current
// formatter.numberStyle = .currency
// return formatter
//}
// DecimalTextField("Enter amount",
// value: defaultRate,
// formatter: self.currencyFormatter)
struct DecimalTextField: UIViewRepresentable {
private var placeholder: String
@Binding var value: Double
private var formatter: NumberFormatter
init(_ placeholder: String,
value: Binding<Double>,
formatter: NumberFormatter ) {
self.placeholder = placeholder
self._value = value
self.formatter = formatter
}
func makeUIView(context: Context) -> UITextField {
let textfield = UITextField()
textfield.keyboardType = .decimalPad
textfield.delegate = context.coordinator
textfield.placeholder = placeholder
textfield.text = formatter.string(for: value) ?? placeholder
textfield.textAlignment = .right
let toolBar = UIToolbar(frame: CGRect(x: 0,
y: 0,
width: textfield.frame.size.width,
height: 44))
let doneButton = UIBarButtonItem(title: "Ok",
style: .done,
target: self,
action: #selector(textfield.doneButtonTapped(button:)))
let space = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace,
target: nil,action: nil)
toolBar.setItems([space, doneButton], animated: true)
textfield.inputAccessoryView = toolBar
return textfield
}
func updateUIView(_ uiView: UITextField, context: Context) {
// Do nothing, needed for protocol
}
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 {
// Allow only numbers and decimal characters
let isNumber = CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string))
let withDecimal = (
string == NumberFormatter().decimalSeparator &&
textField.text?.contains(string) == false
)
if isNumber || withDecimal,
let currentValue = textField.text as NSString?
{
// Update Value
let proposedValue = currentValue.replacingCharacters(in: range, with: string) as String
let decimalFormatter = NumberFormatter()
decimalFormatter.locale = Locale.current
decimalFormatter.numberStyle = .decimal
// Try currency formatter then Decimal formatrer
let number = self.parent.formatter.number(from: proposedValue) ?? decimalFormatter.number(from: proposedValue) ?? 0.0
// Set Value
let double = number.doubleValue
self.parent.value = double
}
return isNumber || withDecimal
}
func textFieldDidEndEditing(_ textField: UITextField,
reason: UITextField.DidEndEditingReason) {
// Format value with formatter at End Editing
textField.text = self.parent.formatter.string(for: self.parent.value)
}
}
}
// MARK: extension for done button
extension UITextField{
@objc func doneButtonTapped(button:UIBarButtonItem) -> Void {
self.resignFirstResponder()
}
}
// MARK: extension for keyboard to dismiss
extension UIApplication {
func endEditing() {
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
|
|
Send action to resign on tap gesture. This will dissmiss keyboard on tap of screen.
struct OTPView: View {
@State var otp: String = ""
var body: some View {
VStack {
SecureField("****", text: $otp)
.font(.system(size: 20, weight: .bold, design: .default))
}
.onTapGesture {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil)
}
}
}
|
|
Thanks!!
The shortest and the only code that worked.
You saved me from a lot of trouble with the following code:
"
.onTapGesture {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil)
}
"
|