Swift TextKit2:如何通过NSTextRange获取UITextView的子串?
解决方法
1. 直接从TextStorage提取字符串(全iOS版本兼容)
NSTextRange是TextKit体系的文本范围类型,对应UITextView底层的textStorage存储范围,无需转换就能直接提取子串:
let textStorage = textView.textStorage let fullText = textStorage.string // 获取适配框架的文本 let stringThatFit: String if let nsRenderedRange = NSRange(rangeOfRenderedText, in: fullText) { stringThatFit = String(fullText[nsRenderedRange]) } else { stringThatFit = "" } // 补全未适配文本的完整范围(从已渲染文本末尾到文档结束) let fullDocumentRange = textLayoutManager.documentRange let rangeOfRemainderText = NSTextRange(location: rangeOfRenderedText.endLocation, end: fullDocumentRange.endLocation) // 获取未适配框架的文本 let stringThatDidNotFit: String if let nsRemainderRange = NSRange(rangeOfRemainderText, in: fullText) { stringThatDidNotFit = String(fullText[nsRemainderRange]) } else { stringThatDidNotFit = "" }
2. 转换NSTextRange为UITextRange后提取(iOS16+专属)
iOS16及以后,UITextView新增了直接转换方法,可将NSTextRange转为UITextRange后使用你找到的text(in:)方法:
// 转换已渲染范围并提取文本 let stringThatFit: String if let renderedUITextRange = textView.textRange(for: rangeOfRenderedText) { stringThatFit = textView.text(in: renderedUITextRange) ?? "" } else { stringThatFit = "" } // 处理未适配文本范围 let fullDocumentRange = textLayoutManager.documentRange let rangeOfRemainderText = NSTextRange(location: rangeOfRenderedText.endLocation, end: fullDocumentRange.endLocation) let stringThatDidNotFit: String if let remainderUITextRange = textView.textRange(for: rangeOfRemainderText) { stringThatDidNotFit = textView.text(in: remainderUITextRange) ?? "" } else { stringThatDidNotFit = "" }
关于NSTextRange与UITextRange的说明
- 两者不能直接比较:UITextRange是UIKit为文本输入组件设计的抽象范围类型,NSTextRange是TextKit(含TextKit2)的底层范围类型,分属不同体系。
- 转换逻辑:iOS16+可直接用
textView.textRange(for:)转换;低版本建议直接通过TextStorage提取,避免复杂的位置转换。
补全你提供的完整代码
以下是结合兼容方案的最终代码:
let textLayoutManager = textView.textLayoutManager! let rangeOfRenderedText: NSTextRange = textLayoutManager.textLayoutFragment(for: .zero)!.rangeInElement let fullDocumentRange = textLayoutManager.documentRange let rangeOfRemainderText: NSTextRange = NSTextRange(location: rangeOfRenderedText.endLocation, end: fullDocumentRange.endLocation) let textStorage = textView.textStorage let fullText = textStorage.string let stringThatFit: String if let nsRenderedRange = NSRange(rangeOfRenderedText, in: fullText) { stringThatFit = String(fullText[nsRenderedRange]) } else { stringThatFit = "" } let stringThatDidNotFit: String if let nsRemainderRange = NSRange(rangeOfRemainderText, in: fullText) { stringThatDidNotFit = String(fullText[nsRemainderRange]) } else { stringThatDidNotFit = "" }
内容的提问来源于stack exchange,提问作者InsightClip
相关产品推荐
相关产品推荐

