Limiting user input to a valid decimal number in Swift
This takes multiple decimals into account by using an NSScanner to test whether the new string would be numeric:
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool {
// Get the attempted new string by replacing the new characters in the
// appropriate range
let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
if newString.length > 0 {
// Find out whether the new string is numeric by using an NSScanner.
// The scanDecimal method is invoked with NULL as value to simply scan
// past a decimal integer representation.
let scanner: NSScanner = NSScanner(string:newString)
let isNumeric = scanner.scanDecimal(nil) && scanner.atEnd
return isNumeric
} else {
// To allow for an empty text field
return true
}
}
Here is a simple example:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.textField.delegate = self
}
//Textfield delegates
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { // return NO to not change text
switch string {
case "0","1","2","3","4","5","6","7","8","9":
return true
case ".":
let array = Array(textField.text)
var decimalCount = 0
for character in array {
if character == "." {
decimalCount++
}
}
if decimalCount == 1 {
return false
} else {
return true
}
default:
let array = Array(string)
if array.count == 0 {
return true
}
return false
}
}
}
All of answers use '.' as valid separator for decimals, but in different localisation it's may be wrong.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard !string.isEmpty else {
return true
}
let currentText = textField.text ?? ""
let replacementText = (currentText as NSString).replacingCharacters(in: range, with: string)
return replacementText.isDecimal()
}
extension String{
func isDecimal()->Bool{
let formatter = NumberFormatter()
formatter.allowsFloats = true
formatter.locale = Locale.current
return formatter.number(from: self) != nil
}
}