iOS自定义SwiftUI键盘如何获取光标前的全部文本?
iOS自定义SwiftUI键盘如何获取光标前的全部文本?
嗨,我之前做自定义键盘的时候也踩过这个坑!确实textDocumentProxy.documentContextBeforeInput有个隐形的字符限制,大概200个左右,官方文档没明说,但实际跑起来就是这样。那些AI键盘能拿到全部文本,其实是用了个小trick——模拟光标移动来绕开这个限制,亲测有效!
核心思路
系统限制的只是单次调用documentContextBeforeInput返回的字符长度,但没禁止我们多次移动光标、多次获取文本再拼接。说白了就是:把光标一步步移到最左边,每次把新获取的文本拼到前面,最后再把光标挪回原来的位置,用户完全感知不到这个过程。
具体实现步骤(附Swift代码)
首先,我们可以给UIInputViewController写个扩展,封装这个获取逻辑——因为SwiftUI的自定义键盘本质上还是要靠UIInputViewController来获取textDocumentProxy:
extension UIInputViewController { func fetchFullTextBeforeCursor() -> String { var fullBeforeText = "" var totalMovedOffset = 0 // 循环向左移动光标,直到无法再移动(到文本开头) while true { // 每次尝试向左移动200字符(刚好卡系统单次返回的上限) guard adjustTextPosition(byCharacterOffset: -200) else { // 已经到最左边了,把最后一段文本补上 if let lastSegment = textDocumentProxy.documentContextBeforeInput { fullBeforeText = lastSegment + fullBeforeText } break } // 把当前光标前的文本拼到结果的前面 if let currentSegment = textDocumentProxy.documentContextBeforeInput { fullBeforeText = currentSegment + fullBeforeText totalMovedOffset += currentSegment.count } } // 把光标移回原来的位置,别让用户察觉我们动过光标 adjustTextPosition(byCharacterOffset: totalMovedOffset) return fullBeforeText } }
然后在SwiftUI的键盘视图里,我们需要用UIViewControllerRepresentable来包装UIInputViewController,才能调用这个方法:
// SwiftUI的键盘容器视图 struct CustomKeyboard: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIInputViewController { let inputVC = UIInputViewController() // 把你的SwiftUI键盘内容嵌入进来 let keyboardContent = KeyboardContentView( fetchFullText: { inputVC.fetchFullTextBeforeCursor() } ) let hostingVC = UIHostingController(rootView: keyboardContent) hostingVC.view.frame = inputVC.view.bounds inputVC.view.addSubview(hostingVC.view) return inputVC } func updateUIViewController(_ uiViewController: UIInputViewController, context: Context) {} } // 你的SwiftUI键盘内容视图示例 struct KeyboardContentView: View { let fetchFullText: () -> String var body: some View { VStack { Button("获取光标前全部文本") { let fullText = fetchFullText() print("拿到的完整前置文本:\(fullText)") // 这里可以处理拿到的文本,比如传给AI生成内容等 } .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) // 这里放你原来的键盘按键布局 } .padding() } }
注意事项
- 一定要恢复光标位置:这是最关键的,如果忘了把光标移回去,用户会一脸懵,体验直接拉胯。
- 特殊输入框兼容:像密码输入框这类安全控件,系统会禁止光标移动和文本获取,这种情况就别强求了,返回空或者提示用户即可。
- 真机测试:模拟器有时候会有奇怪的光标移动bug,建议在真实设备上验证效果。
这个逻辑我自己在多个主流应用(备忘录、微信、Safari)里测过,和那些AI键盘的实现思路一致,完全能拿到光标前的全部文本。
内容来源于stack exchange
相关产品推荐
相关产品推荐

