Ios 在Swift中将用户输入限制为有效的十进制数

Ios 在Swift中将用户输入限制为有效的十进制数,ios,objective-c,ipad,swift,Ios,Objective C,Ipad,Swift,我在objective-c中找到了很多关于如何实现这一点的指南,但我希望看到一种更快速的实现方法 我有一个UITextField,用户在其中输入货币价格。文本字段调用十进制键盘。然而,在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.
    1.0
  • .1
    产生0.1

经过测试并在Swift 3和Swift 4中工作,您还可以执行以下检查

 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
}