Ios 在Swift中将用户输入限制为有效的十进制数
我在objective-c中找到了很多关于如何实现这一点的指南,但我希望看到一种更快速的实现方法 我有一个UITextField,用户在其中输入货币价格。文本字段调用十进制键盘。然而,在iPad上,出现的键盘有一系列非十进制符号 基本上,对于每一次按键,我都希望不可能在字段中键入非数字或任何超过一个小数点的内容。如果输入了一个十进制数,我想不可能输入第二个十进制数。如果删除了小数点,我想确保用户可以再次输入小数点 有没有关于如何在swift中正确执行此操作的想法 我还看到了类似于此处发布的解决方案:Ios 在Swift中将用户输入限制为有效的十进制数,ios,objective-c,ipad,swift,Ios,Objective C,Ipad,Swift,我在objective-c中找到了很多关于如何实现这一点的指南,但我希望看到一种更快速的实现方法 我有一个UITextField,用户在其中输入货币价格。文本字段调用十进制键盘。然而,在iPad上,出现的键盘有一系列非十进制符号 基本上,对于每一次按键,我都希望不可能在字段中键入非数字或任何超过一个小数点的内容。如果输入了一个十进制数,我想不可能输入第二个十进制数。如果删除了小数点,我想确保用户可以再次输入小数点 有没有关于如何在swift中正确执行此操作的想法 我还看到了类似于此处发布的解决方
但我不知道函数应该放在哪里,也不知道应该如何调用它们。每当我尝试在参数中输入NSRange时,我都会收到一个错误,即我没有正确创建范围。以相同的方式执行此操作。下面的代码并不能防止出现多个
,但在其他方面却可以满足您的需要。你想怎么扩展就怎么扩展
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
}
}
下面是一个简单的例子:
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
}
}
}
通过使用NSScanner测试新字符串是否为数字,这将考虑多个小数:
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
}
}
Swift 2版本@Steve Rosenberg的解决方案 如果不需要将输入限制为最多2个小数位数(即,“12.34”正常,“12.345”不正常),则删除开头的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
}
}
}
这里是最简单的方法:
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)
}
这是受wye的答案启发而来的,但更紧凑一些,在我想要数字/十进制字段的地方,它对我很有用。通过修改正则表达式,您可以调整为只接受整数(去掉
?\\d{0,2}
留下^\\d*$
)。同样,如果不想限制小数点后的位数,可以删除该限制(只需将其更改为^\\d*\\.?\\d*
)
这允许在不拒绝输入的情况下构造数字字符串,因此,例如,以下都是有效输入且(newString为NSString)。floatValue
给出有效结果:
(即空字符串)产生0.0
产生0.0
1.01.
产生0.1.1
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
}
}
Swift 3实现此UITextFieldDelegate方法以防止用户键入无效数字:
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个小数位数。以下是我使用的。如果返回false,调用方将使用
textField.deleteBackward()
删除最后一个(有问题的)字符
func isValidNumber(文本:字符串)->Bool{
让validChars:Set=[“0”、“1”、“2”、“3”、“4”、“5”、“6”、“7”、“8”、“9”和“]
return(Set(text).isSubset(of:validChars)和&(text.components(以“.”分隔)。count-1)Bool{
让validChars:Set=[“0”、“1”、“2”、“3”、“4”、“5”、“6”、“7”、“8”、“9”和“]
让validNum=Set(textField.text!).isSubset(of:validChars)和((textField.text!.components)(以“.”分隔)。count-1)改善Naishta在Swift 4中的响应,下面是一个允许您将文本字段长度限制为10个字符的片段(额外奖金-不是帖子创建者要求的)和一个单小数点:
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
}
所有答案都使用“.”作为小数的有效分隔符,但在不同的本地化中,它可能是错误的
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
}
}
SWIFT 3.2和4.0
Chis将用户限制为小数点后两位数,并将用户限制为添加一个小数点。
确保将键盘类型设置为十进制
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// if keyboard type is decimal then apply just one dot
if(textField.keyboardType == .decimalPad)
{
// geting counts of dot
let countdots = (textField.text?.components(separatedBy:".").count)! - 1
// if there is more then one dot then
if(countdots > 0)
{
// creating array by dot
var digitArray = textField.text?.components(separatedBy:".")
let decimalDigits = digitArray![1]
// limiting only 2 digits after decimal point
if(decimalDigits.count > 1 )
{
return false;
}
}
// limiting to only 1 decimal point
if countdots > 0 && string == "."
{
return false
}
}
return true
}
我们可以在不硬编码允许的字符和分隔符的情况下做得更好。特别是分隔符,因为它在不同的地区可能不同。我们还需要注意,用户可能会移动光标并粘贴文本。下面是一个验证函数,它考虑了这一点:
static func validateDecimalNumberText(for textField: UITextField, replacementStringRange: NSRange, string: String) -> Bool {
// Back key
if string.isEmpty {
return true
}
// Allowed charachters include decimal digits and the separator determined by number foramtter's (current) locale
let numberFormatter = NumberFormatter()
numberFormatter.maximumFractionDigits = 2
let allowedCharacters = CharacterSet.decimalDigits.union(CharacterSet(charactersIn: numberFormatter.decimalSeparator))
let characterSet = CharacterSet(charactersIn: string)
// False if string contains not allowed characters
if !allowedCharacters.isSuperset(of: characterSet) {
return false
}
// Check for decimal separator
if let input = textField.text {
if let range = input.range(of: numberFormatter.decimalSeparator) {
let endIndex = input.index(input.startIndex, offsetBy: input.distance(from: input.startIndex, to: range.upperBound))
let decimals = input.substring(from: endIndex)
// If the replacement string contains a decimal seperator and there is already one, return false
if input.contains(numberFormatter.decimalSeparator) && string == numberFormatter.decimalSeparator {
return false
}
// If a replacement string is before the separator then true
if replacementStringRange.location < endIndex.encodedOffset {
return true
} else {
// If the string will exceed the max number of fraction digits, then return false, else true
return string.count + decimals.count <= numberFormatter.maximumFractionDigits
}
}
}
return true
}
以下是Swift 4解决方案:
import struct Foundation.CharacterSet
extension String {
var onlyNumbers: String {
let charset = CharacterSet.punctuationCharacters.union(CharacterSet.decimalDigits).inverted
return components(separatedBy: charset).joined()
}
}
斯威夫特4
使用@SteveRosenberg的答案,并根据我的要求写下
整数的最大数量为4,即9999,最大小数位数限制为2。因此,最大数量可以为9999.99
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// 100 is the tag value of our textfield
/*or you may use "if textfield == myTextField{" if you have an IBOutlet to that textfield */
if textField.tag == 100 {
//max length limit of text is 8
if textField.text!.count > 8 && string != "" {
return false
}
let maxLength = 8
let currentString: NSString = textField.text! as NSString
// Use following code If you are inputting price to that text field and want $ to get inserted automatically at start when user starts typing in that textfield or you may put some other character at start instead of $. Otherwise comment the following 3 lines of if condition code
if currentString.length == 0 {
priceTextField.text = "$"
}
//new string after inserting the new entered characters
let newString: NSString =
currentString.replacingCharacters(in: range, with: string) as NSString
if newString.length > maxLength{
return false
}
if (textField.text!.range(of: ".") != nil) {
let numStr = newString.components(separatedBy: ".")
if numStr.count>1{
let decStr = numStr[1]
if decStr.length > 2{
return false
}
}
}
var priceStr: String = newString as String
if (textField.text!.range(of: "$") != nil) {
priceStr = priceStr.replacingOccurrences(of: "$", with: "")
}
let price: Double = Double(priceStr) ?? 0
if price > 9999.99{
return false
}
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 = decimalCount + 1
}
}
if decimalCount == 1 {
return false
} else {
return true
}
default:
let array = Array(string)
if array.count == 0 {
return true
}
return false
}
}
return true
}
Swift 4.2
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let numberCharSet = CharacterSet(charactersIn: ".").union(CharacterSet.decimalDigits)
let characterSet = CharacterSet(charactersIn: string)
return numberCharSet.isSuperset(of: characterSet)
}
这允许从0到9的数字和小数点
- 只有数字
- 2位小数位
- 无空格
- 小数点可以是点或逗号
如果需要指定小数点,请更改[,]
现在我正在使用这个没有正则表达式的解决方案。希望它有帮助:D
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let currentText = (textField.text as NSString?)?.replacingCharacters(in: range, with: string) else { return true }
if textField == txtFieldWeight || textField == txtFieldHeight {
let newText = currentText.replacingOccurrences(of: ",", with: ".")
let isDecimal = Float(newText) != nil
return isDecimal
}
return true
}
你能写下你正在使用的代码吗?小数点后的位数并不重要,但我想把小数点的数量限制为1,以确保输入的数字始终是有效的双精度。谢谢!我还有一个问题:我如何调用该函数?我试着将该函数放在我的viewDidLoad中,但放在iPad上模拟器我仍然可以输入其他符号等,因此我认为我没有正确调用它。否-在viewDidLoad之外。它会调用自己!使用我给你的整个类。确定我们可以添加删除键或退格大小写-给我一两分钟更新默认大小写。现在应该允许退格/删除键。通常,答案是如果它们包括对代码意图的解释,以及在不引入其他内容的情况下解决问题的原因,则会更有帮助。我应该如何在同一文本字段文本中将“.”值的前一个限制为9的长度。它还允许多个小数点(例如123.123.123),因此使其成为无效的十进制数。
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return Utils.validateDecimalNumberText(for: textField, replacementStringRange: range, string: string)
}
import struct Foundation.CharacterSet
extension String {
var onlyNumbers: String {
let charset = CharacterSet.punctuationCharacters.union(CharacterSet.decimalDigits).inverted
return components(separatedBy: charset).joined()
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// 100 is the tag value of our textfield
/*or you may use "if textfield == myTextField{" if you have an IBOutlet to that textfield */
if textField.tag == 100 {
//max length limit of text is 8
if textField.text!.count > 8 && string != "" {
return false
}
let maxLength = 8
let currentString: NSString = textField.text! as NSString
// Use following code If you are inputting price to that text field and want $ to get inserted automatically at start when user starts typing in that textfield or you may put some other character at start instead of $. Otherwise comment the following 3 lines of if condition code
if currentString.length == 0 {
priceTextField.text = "$"
}
//new string after inserting the new entered characters
let newString: NSString =
currentString.replacingCharacters(in: range, with: string) as NSString
if newString.length > maxLength{
return false
}
if (textField.text!.range(of: ".") != nil) {
let numStr = newString.components(separatedBy: ".")
if numStr.count>1{
let decStr = numStr[1]
if decStr.length > 2{
return false
}
}
}
var priceStr: String = newString as String
if (textField.text!.range(of: "$") != nil) {
priceStr = priceStr.replacingOccurrences(of: "$", with: "")
}
let price: Double = Double(priceStr) ?? 0
if price > 9999.99{
return false
}
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 = decimalCount + 1
}
}
if decimalCount == 1 {
return false
} else {
return true
}
default:
let array = Array(string)
if array.count == 0 {
return true
}
return false
}
}
return true
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let numberCharSet = CharacterSet(charactersIn: ".").union(CharacterSet.decimalDigits)
let characterSet = CharacterSet(charactersIn: string)
return numberCharSet.isSuperset(of: characterSet)
}
let regex = try! NSRegularExpression(pattern: "^[0-9]*([.,][0-9]{0,2})?$", options: .caseInsensitive)
if let newText = (textFieldView.textField.text as NSString?)?.replacingCharacters(in: range, with: string) {
return regex.firstMatch(in: newText, options: [], range: NSRange(location: 0, length: newText.count)) != nil
} else {
return false
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let currentText = (textField.text as NSString?)?.replacingCharacters(in: range, with: string) else { return true }
if textField == txtFieldWeight || textField == txtFieldHeight {
let newText = currentText.replacingOccurrences(of: ",", with: ".")
let isDecimal = Float(newText) != nil
return isDecimal
}
return true
}