Swift 4中单个UITextField实时整数验证并启用按钮的实现
单个文本框实时校验整数输入并控制按钮状态
问题背景
之前做双文本框页面时,靠didEndEditing方法做输入验证,切换输入框时校验通过就启用按钮,逻辑跑起来很顺畅。但现在碰到个特殊场景:页面只有1个文本框和1个按钮,要求文本框仅允许输入整数,输入过程中实时判断——只有当输入全为整数时才启用按钮,按钮初始状态为禁用。
麻烦的是,单个文本框没有其他输入框可切换,用户可能一直停留在这个输入框内,didEndEditing根本触发不了,之前的老方法完全用不上。
我之前尝试的代码
当时想着用shouldChangeCharactersIn方法来处理,写了这段代码:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { print("While entering the characters this method gets called") if textField === timeInputField { //set which characters are valid let invalidCharacters = CharacterSet(charactersIn: "0123456789").inverted //if text isn't empty then hide the error label ("must input a time") if timeInputField.text?.isEmpty == false { timeInputErrorLabel.isHidden = true } //if the string returns values in the range set button to enabled let isValid = string.rangeOfCharacter(from: invalidCharacters, options: [], range: string.startIndex ..< string.endIndex) == nil doWorkoutButtonOutlet.isEnabled = isValid } else { doWorkoutButtonOutlet.isEnabled = true } return true }
但这段代码有明显问题:它只校验了用户刚输入的单个字符,完全没考虑文本框里已有的内容。比如用户先输入"123"再输入"a",单个字符"a"会被拦截,但如果用户先输入"a"再输入"123",或者文本框里已有"12a",按钮状态就会判断错误。而且shouldChangeCharactersIn是在文本更新前调用的,直接用timeInputField.text拿到的还是旧内容,判断逻辑根本不准。
正确的实现思路和代码
核心思路是:计算文本框即将生成的完整内容,对这个内容做全量校验,再同步更新按钮状态,同时控制是否允许输入非数字字符。
完整代码如下:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { guard textField === timeInputField else { doWorkoutButtonOutlet.isEnabled = true return true } // 1. 计算输入后的完整文本 let currentText = textField.text ?? "" let updatedText = (currentText as NSString).replacingCharacters(in: range, with: string) // 2. 校验完整文本:非空且全为整数 let isAllDigits = !updatedText.isEmpty && updatedText.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil // 3. 更新按钮状态与错误提示 doWorkoutButtonOutlet.isEnabled = isAllDigits timeInputErrorLabel.isHidden = isAllDigits || updatedText.isEmpty // 4. 控制输入权限:仅允许数字和退格键 let allowedCharacters = CharacterSet.decimalDigits.union(CharacterSet(charactersIn: "\u{8}")) // \u{8}对应退格的Unicode return string.rangeOfCharacter(from: allowedCharacters.inverted) == nil } // 初始化按钮状态 override func viewDidLoad() { super.viewDidLoad() doWorkoutButtonOutlet.isEnabled = false timeInputErrorLabel.isHidden = true }
代码细节解释
- 计算完整文本:用
NSString的replacingCharacters(in:with:)处理NSRange,避免Swift String和NSRange的转换问题,准确得到输入后的完整内容。 - 全量校验:不再只看单个输入字符,而是校验整个即将生成的文本是否全为数字且非空,不管用户是粘贴、退格还是逐字输入,都能准确判断。
- 按钮状态同步:直接根据完整校验结果设置按钮可用性,空文本时按钮保持禁用状态。
- 输入控制:允许数字和退格键,既避免用户输入无效字符,又支持删除操作。
这样就能实现输入过程中实时校验,只有全整数输入时按钮才启用的需求了。
内容的提问来源于stack exchange,提问作者nc14
相关产品推荐
相关产品推荐

