None of the solutions mentioned here:
How to input currency format on a text field (from right to left) using Swift?
work for me. Please don't mark this as duplicate.
I cannot subclass UITextField to give my own implementation.(This UITextField is also used elsewhere).
The only way that I see is to format replacementText string (user input string) in this method:
override func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentText = textField.text ?? ""
var replacementText = (currentText as NSString).replacingCharacters(in: range, with: string)
}
So when user enters a number it should show like this: 0.00.
When the user taps 1. We should have 0.01
When the user taps 2. We should display 0.12
When the user taps 3. We should display 1.23
When the user taps 4. We should display 12.34
private var enteredText: String = ""
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if enteredText.count > 0 && string == "" && range.length == 1 {
//Handles backspace
enteredText.removeLast()
}
else {
enteredText += string
}
return true
}
func textFieldDidChangeCharacter(textField:UITextField) {
if let number = NumberFormatter().number(from: enteredText) {
let double = number.doubleValue/100.0
let string = String(format: "%0.2f", double)
textField.text = string
}
}
Related
I'm trying to pass the value in my textfield to another view using delegates. This is how I tried to achieve that..
In the 2nd view from where the value is to be passed to main view, this is what I have written..
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let _ = raDelegate {
raDelegate?.durationChanged(hrs: string)
}
return true
}
And in the first view, this is what I have...
func durationChanged(hrs: String) {
myView.durationTextField.text = hrs
}
Now the issue is that if I type in say "5" in the textfield, in the durationChanged function, on applying breakpoint, I get the value in hrs as "5" itself. But when I remove the breakpoint and run, then what gets finally printed to myView.durationTextField.text is 55. Also if I print "7" after that, the "55" gets replaced by "77". While ideally what I should have got was "57".
you use it wrongly.
string on shouldChangeCharactersIn function isn't the value of textField.
for example:
1. textField.text = ""
2. input 5 -> range is (0,0), string is 5, textField.text = "" (The textField.text isn't changed yet, it depends on return value of delegate function, if true it'll replace the textField.text with in range with string)
3. input 7 -> range is (1,0), string is 7, textField.text = "5"
4. input backspace -> range is (2, 1), string is "", textField = "57"
After 4, your textField.text = "5"
so you can revise your code:
let stringAfterChanged = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string)
raDelegate?.durationChanged(hrs: stringAfterChanged)
You used shouldChangeCharactersIn wrongly. update your code with your controller extension
extension YourController: UITextFieldDelegate {
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
if let text = textField.text,
let textRange = Range(range, in: text) {
let updatedText = text.replacingCharacters(in: textRange,
with: string)
raDelegate?.durationChanged(hrs: updatedText)
}
return true
}
}
Or try this workaround
extension YourController: UITextFieldDelegate {
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
let previousText:NSString = textField.text! as NSString
let updatedText = previousText.replacingCharacters(in: range, with: string)
raDelegate?.durationChanged(hrs: updatedText)
return true
}
In iOS 13, when implementing shouldChangeCharactersIn via the UITextfieldDelegate, the application crashes when using the swiping keyboard.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let text = textField.text as NSString? {
let txtAfterUpdate = text.replacingCharacters(in: range, with: string)
textField.text = txtAfterUpdate
}
return false
}
Is this an Apple bug?
I was able to reproduce this - if you mutate the state of the text on a UITextField during swipe entry - and only during swipe entry, it'll attempt to reinsert the swiped content (even if you return false), which retriggers your delegate event, which kicks off the recursive cycle.
It's a bit of a hack but you could catch it with something like
private var lastEntry: String?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.count > 1 && string == lastEntry { // implies we're swiping or pasting
print("Caught unwanted recursion")
return
}
lastEntry = string
if let text = textField.text as NSString? {
let txtAfterUpdate = text.replacingCharacters(in: range, with: string)
textField.text = txtAfterUpdate
}
return false
}
It'll stop users from pasting/swiping the same thing twice in a row, but at least it'll let them swipe while Apple fixes their problem.
I used UIPasteboard to identify when the user is pasting and then leave the text as the user entered using the swipe like this:
public func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
//check if the user used swipe keyboard
if string.count > 1 && string != UIPasteboard.general.string ?? "" {
return true
}
//do the text treatment
return false
}
I also realized that the TextField only accepts static strings when using swipe keyboard.
Hope it Helps.
Before setting text you can reset delegate and after set it to self again.
But this solution has one problem if textfield is empty - text will be doubled.
Му code example:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentText: String = textField.text ?? ""
if #available(iOS 13, *) {
textField.delegate = nil
let resultText = editedText
textField.text = resultText
if currentText.isEmpty, textField.text != resultText {
textField.text = resultText
}
textField.delegate = self
} else {
textField.text = input.result
}
return false
}
I have the app for Celectial navigation calculations, I have converted in code textField.text to Double, but some times app crashing if user input some fields like "1.0" and some like "1", in result app crashing because can't deduct Int and Double, to be sure I want to restrict user to input only decimal digits "1.0". The best way for me is to code something like if the user enters for example "1" automatically after pressing the done button, add ".0" to get 1.0?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let allowedCharacters = "-1234567890."
let allowedCharacterSet = CharacterSet(charactersIn: allowedCharacters)
let typedCharactersSet = CharacterSet(charactersIn: string)
return allowedCharacterSet.isSuperset(of: typedCharactersSet)
}
func TextField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = latDegTextField.text else { return true }
let count = text.count + string.count - range.length
return count == 2
}
First of all use this method from HERE which is
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.text != "" || string != "" {
let res = (textField.text ?? "") + string
return Double(res) != nil
}
return true
}
And in your done button action add this:
#IBAction func btnDoneTapped(_ sender: Any) {
print(tf.text)
guard let obj = Double(tf.text!) else { return }
print(obj)
}
And when you enter 1 and press done button print(tf.text) will print Optional("1") and print(obj) will print 1.0
Use this code :-
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//Will prevent user from entering space as first character
let enteredCharString = "\(textField.text ?? "")\(string )"
if enteredCharString.trimmingCharacters(in: .whitespaces).count == 0 {
return false
}
switch textField {
case txt_Ammount:
if txt_Ammount.text != "" || string != "" {
let res = (txt_Ammount.text ?? "") + string
return Double(res) != nil
}
default:
true
}
return true
}
How do we disallow empty input on textField?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let oldText = textField.text!
let stringRange = Range(range, in:oldText)!
let newText = oldText.replacingCharacters(in: stringRange, with: string)
doneBarButton.isEnabled = !newText.isEmpty
return true
}
Is there other ways? or using other textField delegate methods to do this?
Yes, this is basically how you do it.
Alternatively, if all you want to know if the field is empty or not, just figure out the length of the result:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let length = (textField.text?.count ?? 0) + string.count - range.length
doneBarButton.isEnabled = length > 0
return true
}
You may also want to set enablesReturnKeyAutomatically for the text field to true, so that the keyboard doesn’t enable the “return” key unless there is text in the field.
Am very new to swift language, Any help is appreciable, Here is my code
public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField.text!.isEmpty {
textField.text = "0.00"
}
let range2 = string.rangeOfCharacterFromSet(NSMutableCharacterSet.decimalDigitCharacterSet())
if range2 != nil{
// textField.text = "M"
var f = textField.text?.floatValue
f=f!*10.0
f=f!+(string.floatValue/100.0)
textField.text = NSString(format: "%.2f", f!) as String
return false;
}
var f = textField.text?.floatValue
f=f!/10.0
textField.text = NSString(format: "%.2f", f!) as String
return false;
}
When am entering the numbers in texfield it's taking the numbers upto infinity. Please help me how to restrict the textfield to allow only 4 digits in my code.
First point, shouldChangeCharactersInRange is not a method for side effects. It should not be mutating any state, it should not change the value of the text field. It should merely return either true or false.
As I understand it, you want to disallow entry of more than four digits to the left of the decimal point.
extension ViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string)
let number = Float(newText)
return newText.isEmpty || newText == "." || number != nil && number! < 10000
}
}