将用户input限制在Swift中有效的十进制数字

我已经find了很多关于如何在objective-c中执行此操作的指南,但是我希望看到更多的以Swift为导向的方法。

我有一个用户input货币价格的UITextField。 文本框称为十进制键盘。 但是,在iPad上,出现的键盘具有整个范围的非十进制符号。

基本上,对于每一个按键,我想使一个非数字或超过一个小数的任何东西都不可能被input到字段中。 如果input小数点,我想不能input第二个小数点。 如果小数被删除,我想确保用户可以再次input小数。

任何想法如何正确地做到这一点在迅速?

我也看到像这里发布的解决scheme: 限制UITextField一位小数点Swift但我不知道在哪里放置的function,或者我应该怎样称呼他们。 每当我尝试在参数中放入NSRange,我收到一个错误,我不正确创build一个范围。

这是一个简单的例子:

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 } } } 

Swift 2版本的@Steve Rosenberg的解决scheme

如果您不需要将input限制为最大2个小数位(即“12.34”,“12.345”不是OK),则在开始处删除4行。

 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 false to not change text // max 2 fractional digits allowed let newText = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string) let regex = try! NSRegularExpression(pattern: "\\..{3,}", options: []) let matches = regex.matchesInString(newText, options:[], range:NSMakeRange(0, newText.characters.count)) guard matches.count == 0 else { return false } switch string { case "0","1","2","3","4","5","6","7","8","9": return true case ".": let array = textField.text?.characters.map { String($0) } var decimalCount = 0 for character in array! { if character == "." { decimalCount++ } } if decimalCount == 1 { return false } else { return true } default: let array = string.characters.map { String($0) } if array.count == 0 { return true } return false } } } 

这通过使用一个NSScanner来testing新的string是否是数字来考虑多个小数:

 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 } } 

这是最简单的方法:

 func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { if (textField.text?.componentsSeparatedByString(".").count > 1 && string == ".") { return false } return string == "" || (string == "." || Float(string) != nil) } 

这是由Y的回答启发,但是有点紧凑,并为我工作,我想要一个数字/十进制字段。 您可以通过修改正则expression式来适应接受整数(拿出.?\\d{0,2}给你留下^\\d*$ )。 同样,如果您不想限制小数点后面的位数,则可以删除该限制(只要将其更改为^\\d*\\.?\\d*

  func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { let newString = (_timeQuantityField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string) let decimalRegex = try! NSRegularExpression(pattern: "^\\d*\\.?\\d{0,2}$", options: []) let matches = decimalRegex.matchesInString(newString, options: [], range: NSMakeRange(0, newString.characters.count)) if matches.count == 1 { return true } return false } 

这允许数字string被构造而没有任何拒绝input,例如,以下是所有有效的input和(newString as NSString).floatValue给出了一个有效的结果):

  • (即空string)得到0.0
  • . 产量为0.0
  • 1.产量1.0
  • .1产生0.1

做同样的事情 下面的代码不能防范多重. 但其他方面做你想要的。 按照你的意愿扩展它。

 class Foo: NSObject, UITextFieldDelegate { func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { var result = true if countElements(string) > 0 { let numericInput = NSCharacterSet(charactersInString: "0123456789.-").invertedSet if let badRange = string.rangeOfCharacterFromSet(numericInput) { let substring = string.substringToIndex(badRange.startIndex) let oldString: NSString = textField.text // necessary so we can use the NSRange object passed in. textField.text = oldString.stringByReplacingCharactersInRange(range, withString: substring) result = false } } return result } } 

Swift 3实现这个UITextFieldDelegate方法来防止用户input一个无效的数字:

 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let text = (textField.text ?? "") as NSString let newText = text.replacingCharacters(in: range, with: string) if let regex = try? NSRegularExpression(pattern: "^[0-9]*((\\.|,)[0-9]{0,2})?$", options: .caseInsensitive) { return regex.numberOfMatches(in: newText, options: .reportProgress, range: NSRange(location: 0, length: (newText as NSString).length)) > 0 } return false } 

它使用逗号或点作为小数点分隔符,并允许2个小数位。

Swift 3和Swift 4中testing并运行,您还可以执行以下检查

  func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { let existingTextHasDecimalSeparator = textField.text?.rangeOfString(".") let replacementTextHasDecimalSeparator = string.rangeOfString(".") if existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil { return false } else { return true } } 

这是我使用的。 如果返回false,调用者将使用textField.deleteBackward()移除最后一个(违规的)字符。

 func isValidNumber(text: String) -> Bool { let validChars: Set<Character> = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."] return (Set(text).isSubset(of: validChars) && ((text.components(separatedBy: ".").count - 1) <= 1)) } 

或者你可以在这个function中做到这一切:

 func isValidNumber2(textField: UITextField) -> Bool { let validChars: Set<Character> = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."] let validNum = Set(textField.text!).isSubset(of: validChars) && ((textField.text!.components(separatedBy: ".").count - 1) <= 1) if !validNum { textField.deleteBackward() } return (validNum) } 

两者都简短,清晰,简单,高效。 似乎第二个更清洁…意见?

 func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { if (range.location == 0 && string == ".") { return false } else if string == "."{ if textField.text?.componentsSeparatedByString(".").count > 1{ return false } } let aSet = NSCharacterSet(charactersInString:"0123456789.").invertedSet let compSepByCharInSet = string.componentsSeparatedByCharactersInSet(aSet) let numberFiltered = compSepByCharInSet.joinWithSeparator("") return string == numberFiltered } 

Swift 4中改进了Naishta的响应,下面是一个代码片段,它允许您将文本字段的长度限制为10个字符( 额外的奖励 – 不是由创build者请求的 )和一个小数点

 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { guard let text = textField.text else { return true } // Max 10 characters. let newLength = text.count + string.count - range.length if newLength > 10 { return false } // Max one decimal point. let existingTextHasDecimalSeparator = text.range(of: ".") let replacementTextHasDecimalSeparator = string.range(of: ".") if existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil { return false } return true }