You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 17:06:28