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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:33:19