如何在Swift中限制UITextField只接受数字?

94

我希望用户在UITextField中只能输入数字值。在iPhone上,我们可以显示数字键盘,但在iPad上,用户可以切换到任何键盘。

有没有办法限制用户在UITextField中只能输入数字值?


注意:这是一个旧问题,有很多回答。超过三分之一的回答是错误的。甚至一些得到高赞的回答也是错误的。请不要发布新的答案,除非它以某种方式找到了一种大大改进的方法来解决这个问题,而这种方法还没有被展示出来。确保您的答案在所有情况下都能正常工作。它必须适用于iPhone和iPad。当用户将文本粘贴到文本字段中时,它必须起作用。如果用户有外部键盘,它必须起作用。如果用户在键入或粘贴文本之前更改了文本字段中的文本选择,则必须起作用。它必须正确支持小数(一个适合用户区域设置的小数分隔符)。


你需要修改输入的文本。 - Blind Ninja
是的,但在iPad上,即使我们设置了数字键盘,用户仍然可以输入字母。 - user5034941
你说的修改文本是什么意思? - user5034941
请查看此链接:https://dev59.com/dGcs5IYBdhLWcg3wUCYI#12944946 - Blind Ninja
https://dev59.com/EV8d5IYBdhLWcg3wXBJe - Keyur Akbari
39个回答

109

Swift 3.0及以上版本的解决方案

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let allowedCharacters = CharacterSet.decimalDigits
    let characterSet = CharacterSet(charactersIn: string)
    return allowedCharacters.isSuperset(of: characterSet)
}

7
您的视图控制器应符合 UITextFieldDelegate 协议,在 viewDidLoad 方法中设置 yourNumbersTextField.delegate = self - Manee.O.H
什么是range? - user924
1
为什么你没有提到UITextFieldDelegate? - user924
@user924 范围是由 UITextField 委托方法返回的。 - Hiren Panchal
@user924 提到 UITextFieldDelegate 是基本要求。 - Hiren Panchal

82

以下是我的意见。(只针对Swift 2进行测试)

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

  let aSet = NSCharacterSet(charactersInString:"0123456789").invertedSet
  let compSepByCharInSet = string.componentsSeparatedByCharactersInSet(aSet)
  let numberFiltered = compSepByCharInSet.joinWithSeparator("")
  return string == numberFiltered

}

这个要求更为严格,不能有小数点。

希望对你有所帮助 :)

PS:我假设你已经照顾好了代理。

更新:Swift 3.0

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let aSet = NSCharacterSet(charactersIn:"0123456789").inverted
    let compSepByCharInSet = string.components(separatedBy: aSet)
    let numberFiltered = compSepByCharInSet.joined(separator: "")
    return string == numberFiltered
}

我该如何限制UITextfield仅接收数字,并将数字数量限制在6到8之间? - Marco Almeida
你可以将aSet设置为CharacterSet.decimalDigits.invertedSet - yesthisisjoe
非常适合我的需求。谢谢。 - May Yang
为什么您没有提到 UITextFieldDelegate? - user924
你还应该添加一个检查 text == "" 的条件,这样用户就可以向后删除文本了。 - thecoolwinter

33

在Swift 4.1和Xcode 9.4.1中,将 UITextFieldDelegate 添加到您的类中

class YourViewController: UIViewController, UITextFieldDelegate

然后在你的 viewDidLoad() 中编写以下代码

mobileNoTF.delegate = self

编写此文本字段委托函数

//MARK - UITextField Delegates
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    //For mobile numer validation
    if textField == mobileNoTF {
        let allowedCharacters = CharacterSet(charactersIn:"+0123456789 ")//Here change this characters based on your requirement
        let characterSet = CharacterSet(charactersIn: string)
        return allowedCharacters.isSuperset(of: characterSet)
    }
    return true
}

如何解决 Binary operator '==' cannot be applied to operands of type 'UITextField' and '() -> ()' 的问题? - newswiftcoder
@iOS,它是如何工作的?我已经添加了所有内容,但我仍然无法输入任何内容。该字段允许任何字符,但textField函数未被触发。 - Smit

16

iPhone

在你获取这些值的任何UITextField中,当有人点击文本框时,你可以指定想要出现的键盘类型。例如:一个只有数字的键盘。就像这个屏幕截图:

enter image description here

iPad

iPad不支持数字键盘,所以你的选择是要么不支持iPad,要么在提交后验证该字段,或者按照这里的其他建议之一,在iPad上运行时创建相同的行为。


19
这并没有回答这个问题。即使使用数字键盘,iOS 仍然允许用户将非数字值粘贴到 UITextField 中,或者允许 iPad 切换到非数字键盘。 - Rob
这只是对这个问题的简单回答!并不是正确的答案!我知道! - Jamil Hasnine Tamim
2
这并不是对问题的任何答案,既不简单也不适当。问题并不是在问如何更改键盘类型,而是在问如何限制输入。这两者是不同的。 - Rob
不是的,因为在UITextField上设置的首选键盘类型并不是输入文本到文本字段中的唯一方式。正如Rob所说,您还可以将文本粘贴到其中,使用外部键盘,并且有些设备无论如何都可以切换到不同的键盘类型。 - Andrew M
1
OP明确要求提供除更改键盘类型之外的其他解决方案。“在iPhone上,我们可以显示数字键盘,但在iPad上,用户可以切换到任何键盘。” - Ferdz
显示剩余2条评论

14

Swift 2.0

只允许在uitextfield中输入数字和一个小数点"."。

func textField(textField: UITextField,shouldChangeCharactersInRange range: NSRange,replacementString string: String) -> Bool
{
    let newCharacters = NSCharacterSet(charactersInString: string)
    let boolIsNumber = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newCharacters)
    if boolIsNumber == true {
        return true
    } else {
        if string == "." {
            let countdots = textField.text!.componentsSeparatedByString(".").count - 1
            if countdots == 0 {
                return true
            } else {
                if countdots > 0 && string == "." {
                    return false
                } else {
                    return true
                }
            }
        } else {
            return false
        }
    }
}

谢谢 - 非常好(我正在寻找一个只允许1个“。”的解决方案) - derdida

9

在Swift 3中,文本字段中可以接受只有一个(.)小数点的十进制值。

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let inverseSet = NSCharacterSet(charactersIn:"0123456789").inverted

    let components = string.components(separatedBy: inverseSet)

    let filtered = components.joined(separator: "")

    if filtered == string {
        return true
    } else {
        if string == "." {
            let countdots = textField.text!.components(separatedBy:".").count - 1
            if countdots == 0 {
                return true
            }else{
                if countdots > 0 && string == "." {
                    return false
                } else {
                    return true
                }
            }
        }else{
            return false
        }
    }
}

这是唯一一个(限制为一位小数)我能够让它工作的,太棒了! - Chris Allinson
实际上,我已经修改它,以允许一个逗号(根据FR)。 - Chris Allinson
7
请注意,并非所有语言环境都使用“.”作为小数分隔符。您可能希望使用Locale.current.decimalSeparator代替。 - Volker Voecking

7

像这样扩展您的视图控制器:

class MyViewController: UIViewController, UITextFieldDelegate

在viewDidLoad函数中按照以下方式扩展您的文本字段:
myTextField.delegate = self

然后使用以下函数:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let isNumber = CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string))
    let withDecimal = (
        string == NumberFormatter().decimalSeparator &&
        textField.text?.contains(string) == false
    )
    return isNumber || withDecimal
}

现在,这将确保用户只能输入十进制数字。

Swift 4+仅接受数字并接受一个分隔符


7
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    // return true if the replacementString only contains numeric characters
    let digits = NSCharacterSet.decimalDigitCharacterSet()
    for c in string {
        if !digits.characterIsMember(c) {
            return false
        }
    }

    return true
}

即使用户切换键盘或试图将非数字字符串粘贴到文本字段中,此解决方案也可以使用。

请确保设置适当文本字段的delegate属性。


1
使用先前答案中建议的[string intValue]将不起作用,因为该属性可能为真(非零),即使string包含非数字字符。根据文档,只有当“字符串不以有效的十进制文本表示形式开始时”,intValue才为0。 - ndmeiri

5

使用数字格式化器

Swift 4.x

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
      let s = NSString(string: textField.text ?? "").replacingCharacters(in: range, with: string)
      guard !s.isEmpty else { return true }
      let numberFormatter = NumberFormatter()
      numberFormatter.numberStyle = .none
      return numberFormatter.number(from: s)?.intValue != nil
 }

谢谢!这个比其他很多更好,因为它允许使用小数,但不允许任意使用小数。 - Andy Weinstein

4

这里有一个简单的解决方案,您需要将“Editing changed”事件连接到控制器中的此方法。

Swift 4

@IBAction func valueChanged(_ sender: UITextField) {
    if let last = sender.text?.last {
        let zero: Character = "0"
        let num: Int = Int(UnicodeScalar(String(last))!.value - UnicodeScalar(String(zero))!.value)
        if (num < 0 || num > 9) {
            //remove the last character as it is invalid
            sender.text?.removeLast()
        }
    }
}

如果委托已经被 Rx 使用,这是一个很好的解决方案。 - S-K'
如果我添加空格或符号,它会崩溃。 - karthikeyan
如果用户在输入之前移动光标或选择一些文本,则此方法将无效。该代码允许用户粘贴任意文本,只要最后一个字符是数字即可。 - HangarRash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接