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

iOS自动OTP验证问题:OTP未显示在键盘工具栏

解决iOS多个OTP输入框自动填充不显示的问题

核心原因

iOS的AutoFill OTP功能是为单个输入框设计的。当你使用多个拆分的输入框时,系统无法识别如何将完整的OTP字符串分配到每个独立输入框中,因此不会触发键盘上方的自动填充提示。

解决方案

方案1:使用隐藏输入框接收OTP(推荐)

通过一个隐藏的UITextField接收系统自动填充的OTP,再将内容拆分到多个显示用的输入框中,兼顾自动填充和用户手动输入体验。

代码实现:

  1. 添加隐藏输入框并配置:
private let otpReceiverTextField = UITextField()

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 配置用于接收OTP的隐藏输入框
    otpReceiverTextField.isHidden = true
    otpReceiverTextField.textContentType = .oneTimeCode
    otpReceiverTextField.keyboardType = .default
    otpReceiverTextField.addTarget(self, action: #selector(otpReceived(_:)), for: .editingChanged)
    view.addSubview(otpReceiverTextField)
    
    // 配置四个显示用的输入框:移除.oneTimeCode设置,保留数字键盘
    [otp1, otp2, otp3, otp4].forEach { textField in
        textField?.keyboardType = .numberPad
        textField?.addTarget(self, action: #selector(didEdited), for: .editingChanged)
    }
    
    // 让隐藏输入框成为第一响应者,触发OTP自动填充提示
    otpReceiverTextField.becomeFirstResponder()
}
  1. 处理OTP自动填充后的拆分逻辑:
@objc private func otpReceived(_ textField: UITextField) {
    guard let otp = textField.text, otp.count == 4 else { return }
    
    // 将完整OTP拆分到四个输入框
    otp1.text = String(otp[otp.startIndex])
    otp2.text = String(otp[otp.index(after: otp.startIndex)])
    otp3.text = String(otp[otp.index(otp.startIndex, offsetBy: 2)])
    otp4.text = String(otp[otp.index(otp.startIndex, offsetBy: 3)])
    
    // 填充完成后隐藏键盘
    otpReceiverTextField.resignFirstResponder()
}
  1. 修改原有didEdited方法,同步手动输入到隐藏输入框:
@objc func didEdited(textField: UITextField) {
    let text = textField.text
    
    // 同步四个输入框的内容到隐藏输入框
    let combinedOTP = (otp1.text ?? "") + (otp2.text ?? "") + (otp3.text ?? "") + (otp4.text ?? "")
    otpReceiverTextField.text = combinedOTP
    
    // 原有光标跳转逻辑保持不变
    if text?.count == 1 {
        switch textField {
        case otp1:
            otp2.becomeFirstResponder()
        case otp2:
            otp3.becomeFirstResponder()
        case otp3:
            otp4.becomeFirstResponder()
        case otp4:
            otp4.resignFirstResponder()
        default:
            break
        }
    }

    if text?.count == 0 {
        switch textField {
        case otp1:
            otp1.becomeFirstResponder()
        case otp2:
            otp2.text = ""
            otp1.becomeFirstResponder()
        case otp3:
            otp3.text = ""
            otp2.becomeFirstResponder()
        case otp4:
            otp4.text = ""
            otp3.becomeFirstResponder()
        default:
            break
        }
    }
}

方案2:手动监听短信通知提取OTP

如果必须保留多个独立输入框,可以通过监听系统短信通知,手动提取OTP并填充。

代码实现:

  1. 请求通知权限:
override func viewDidLoad() {
    super.viewDidLoad()
    
    // 请求通知权限以监听短信
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, _ in
        if granted {
            DispatchQueue.main.async {
                UIApplication.shared.registerForRemoteNotifications()
            }
        }
    }
    
    // 原有输入框配置逻辑...
}
  1. 添加通知监听与OTP提取:
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // 监听系统短信通知
    NotificationCenter.default.addObserver(self, selector: #selector(extractOTPFromSMS(_:)), name: NSNotification.Name("NSSMSMessageNotification"), object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name("NSSMSMessageNotification"), object: nil)
}

@objc private func extractOTPFromSMS(_ notification: Notification) {
    guard let userInfo = notification.userInfo, let smsContent = userInfo["message"] as? String else { return }
    
    // 正则匹配4位数字OTP(根据实际长度调整)
    let regexPattern = "\\d{4}"
    guard let regex = try? NSRegularExpression(pattern: regexPattern) else { return }
    let matches = regex.matches(in: smsContent, range: NSRange(smsContent.startIndex..., in: smsContent))
    
    if let match = matches.first, let otpRange = Range(match.range, in: smsContent) {
        let otp = String(smsContent[otpRange])
        // 填充到四个输入框
        if otp.count == 4 {
            otp1.text = String(otp[0])
            otp2.text = String(otp[1])
            otp3.text = String(otp[2])
            otp4.text = String(otp[3])
        }
    }
}

额外检查项

  • 确认短信中的App名称与Xcode项目设置的Bundle Display Name完全一致,系统会通过名称匹配确定是否为当前App的OTP。
  • 设备设置中,确保「密码自动填充」已开启,且勾选了「短信验证码」选项(iOS16及以上版本)。
  • 不要在多个显示用输入框上设置.oneTimeCode,仅在接收OTP的单个输入框上配置该属性。

内容的提问来源于stack exchange,提问作者Arish

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:44:55