Swift实现OTP输入时CollectionView如何自动跳转光标至下一文本框
Swift实现OTP输入框自动跳转光标方案
现有代码存在的问题
- 全局缓存
arrayOfCells的写法有问题:UICollectionView的Cell存在复用机制,在cellForItemAt里反复累加Cell实例,会导致数组内存在重复、已失效的Cell引用,后续取值会出异常 - 自动跳转逻辑缺失:当前仅在编辑结束时才校验输入内容,无法实现输入1位后立刻跳转下一个框的交互
- 未处理删除回退逻辑:用户输错内容按删除键时,无法自动跳回上一个输入框,体验不符合常规OTP输入习惯
具体实现步骤
1. 修复Cell获取逻辑
删除全局的arrayOfCells变量,不要手动缓存Cell实例,需要获取对应位置的Cell时,直接通过UICollectionView的接口获取,从根源避免复用导致的异常:
// 新增工具方法,获取指定位置的OTP Cell private func getOtpCell(at row: Int) -> OtpCollectionViewCell? { let otpCount = 6 // 可替换为动态长度变量 guard row >= 0, row < otpCount else { return nil } let targetIndexPath = IndexPath(row: row, section: 0) return collectionView.cellForItem(at: targetIndexPath) as? OtpCollectionViewCell }
修正cellForItemAt方法,补上复用重置、数字键盘配置:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OtpCollectionViewCell", for: indexPath) as! OtpCollectionViewCell cell.otpTextField.tag = indexPath.row cell.otpTextField.delegate = self cell.otpTextField.text = "" // 复用时清空旧输入内容 cell.otpTextField.keyboardType = .numberPad // 直接唤起数字键盘,减少非数字输入校验成本 cell.otpTextField.textAlignment = .center return cell }
2. 实现自动跳转+删除回退逻辑
核心逻辑放在textField(_:shouldChangeCharactersIn:replacementString:)代理方法中,实时监听输入变化:
- 输入新字符时,当前框填入内容后自动跳转下一个框,到最后一位时自动收键盘、拼接验证码调用接口
- 按删除键时,当前框清空后自动跳回上一个框,同步清空上一个框的内容
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { // 禁止输入空格 guard string != " " else { return false } let currentText = textField.text ?? "" guard let stringRange = Range(range, in: currentText) else { return false } let updatedText = currentText.replacingCharacters(in: stringRange, with: string) // 限制只能输入1位数字 guard updatedText.count <= 1, updatedText.allSatisfy({ $0.isNumber }) else { return false } let otpCount = 6 // 可替换为动态长度变量 let currentIndex = textField.tag // 输入新字符场景 if !string.isEmpty { textField.text = string if currentIndex < otpCount - 1 { // 跳转至下一个输入框 let nextCell = getOtpCell(at: currentIndex + 1) nextCell?.otpTextField.becomeFirstResponder() } else { // 已到最后一位,收起键盘 textField.resignFirstResponder() // 拼接完整OTP码调用验证接口 let fullOtpCode = (0..<otpCount).compactMap { getOtpCell(at: $0)?.otpTextField.text }.joined() performAPICall(with: fullOtpCode) } } // 删除字符场景 else { textField.text = "" if currentIndex > 0 { // 跳转回上一个输入框 let prevCell = getOtpCell(at: currentIndex - 1) prevCell?.otpTextField.becomeFirstResponder() prevCell?.otpTextField.text = "" } } return false }
3. 清理冗余逻辑
删除原来textFieldDidEndEditing中遍历Cell拼接验证码、触发接口调用的逻辑,现在输入到最后一位时会实时触发校验,不需要等编辑结束,响应速度更快。
体验优化补充
- 页面展示后默认唤起第一个输入框的键盘,减少用户点击步骤:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let firstCell = getOtpCell(at: 0) firstCell?.otpTextField.becomeFirstResponder() }
- 给输入框增加选中态样式(比如聚焦时显示高亮边框、放大字号),让用户清晰感知当前输入位置
- 如果需要支持动态OTP长度,把代码中写死的
6替换成你自己的长度变量即可,不需要改其他逻辑
内容的提问来源于stack exchange,提问作者Anilkumar iOS Developer
相关产品推荐
相关产品推荐

