iOS键盘扩展:如何检测当前激活文本框的输入类型?
Hey there! I’ve dealt with this exact headache when building custom iOS keyboard extensions—those nil values for textContentType and keyboardType can feel like a dead end. Let’s walk through why this happens and how you can work around it to show that "@" button when needed.
Why Are These Properties Returning Nil?
- Host App Limitations: Not all apps (including some system apps like Messages) expose
textContentTypeorkeyboardTypeto keyboard extensions. Some developers skip setting these traits, and certain system input fields restrict access for privacy or design reasons. - Timing Issues: Trying to access these properties too early (like in
textWillChange) might mean the proxy hasn’t fully initialized the values yet.
Practical Workarounds
1. Infer Input Type from Context Text
Since direct access isn’t always reliable, use the surrounding text to guess the input field’s purpose. For Messages’ address field, you can look for context clues like "To:", "收件人", or existing "@" characters:
override func textDidChange(_ textInput: UITextInput?) { super.textDidChange(textInput) let beforeContext = textDocumentProxy.documentContextBeforeInput ?? "" let afterContext = textDocumentProxy.documentContextAfterInput ?? "" // Check for address/recipient field clues let isRecipientField = beforeContext.contains("To:") || beforeContext.contains("收件人") || beforeContext.contains("@") || afterContext.contains("@") // Toggle your "@" button based on the check if isRecipientField { showAtButton() // Replace with your button show logic } else { hideAtButton() // Replace with your button hide logic } }
This method isn’t 100% foolproof, but it works surprisingly well for most common system and third-party app scenarios.
2. Check Properties at the Right Lifecycle Point
Sometimes the values just aren’t ready when textWillChange fires. Try accessing keyboardType or textContentType in the didBecomeActive method, which triggers when your keyboard becomes the active input method:
override func didBecomeActive() { super.didBecomeActive() // Check keyboard type first if available if let keyboardType = textDocumentProxy.keyboardType { if keyboardType == .emailAddress { showAtButton() return } } // Fall back to context check if keyboard type is nil let beforeContext = textDocumentProxy.documentContextBeforeInput ?? "" if beforeContext.contains("To:") || beforeContext.contains("收件人") { showAtButton() } }
This combines direct property checks with context inference for better coverage.
3. Combine Multiple Checks for Reliability
For the best results, layer your checks: first try textContentType/keyboardType, then fall back to context analysis. This way you cover cases where the host app does expose the traits, and cases where it doesn’t.
Final Notes
- Test across multiple apps: System apps like Mail might expose
textContentType = .emailAddress, while Messages’ recipient field won’t—so your context check will be the fallback here. - Be flexible: Some third-party apps might have unique context clues, so you can expand your check logic as you test more scenarios.
内容的提问来源于stack exchange,提问作者Awais Mobeen

