在 UITextView 中拆分段落并保存范围
Split Paragraphs and Save Ranges in UITextView
我正在尝试使用我在下面键入的代码将段落拆分为一组组件。不过,我在使用 Ranges 元素时遇到了问题。我的问题是,如果用户多次键入一个字符串,它 returns 是第一次范围,而不是我想要的范围。
示例:
第 1 段:香蕉
第2款:苹果
第 3 段:香蕉
第 1 段和第 3 段都有第 1 段的范围。解决此问题的更明智的方法是什么?
lazy var models: [ParagraphModel] = []
struct ParagraphModel {
let text: String
let range: Range<String.Index>
}
func getParagraphs(){
let components = textView.text.components(separatedBy: "\n")
let models = components.compactMap { component -> ParagraphModel? in
if let range = textView.text.range(of: component) {
return ParagraphModel(text: component, range: range)
}
return nil
}
self.models = models
}
来自@Rob 的更新代码:
func getParagraphModel() {
guard let text = noteContents.text else { return }
var currentModels: [ParagraphModel] = []
text.enumerateSubstrings(in: text.startIndex..., options: .byParagraphs) { substring, range, _, stop in
if let substring = substring, !substring.isEmpty,
let textRange = self.noteContents.text.range(of: substring)
{
currentModels.append(ParagraphModel(text: substring, range: textRange))
}
}
self.models = currentModels
}
键入示例文本时,控制台的打印输出为:
[Project.ViewController.ParagraphModel(text: "Banana", range: Range(Swift.String.Index(_rawBits: 0)..<Swift.String.Index(_rawBits: 393216))), Project.ViewController.ParagraphModel(text: "Apple", range: Range(Swift.String.Index(_rawBits: 458752)..<Swift.String.Index(_rawBits: 786432))), Project.ViewController.ParagraphModel(text: "Banana", range: Range(Swift.String.Index(_rawBits: 0)..<Swift.String.Index(_rawBits: 393216)))]
你的代码片段中的问题是你总是会找到目标字符串的第一次出现。因此,当它搜索第三段“Banana”时,它将找到该字符串的第一次出现。
我建议 text.enumerateSubstrings(in:options:_:)
获取 text
内的段落范围。这样会枚举出子串及其各自的范围,避免了你自己去搜索文本,避免了同一个字符串出现多次可能出现的问题。
因此:
func getParagraphs() {
guard let textView = textView, let text = textView.text else { return }
models = []
text.enumerateSubstrings(in: text.startIndex..., options: .byParagraphs) { substring, range, _, _ in
guard let substring = substring, !substring.isEmpty else {
return
}
self.models.append(ParagraphModel(text: substring, range: range))
}
}
产生:
[
MyApp.ParagraphModel(text: "Paragraph 1: Banana", range: Range(Swift.String.Index(_rawBits: 0)..<Swift.String.Index(_rawBits: 1245184))),
MyApp.ParagraphModel(text: "Paragraph 2: Apple", range: Range(Swift.String.Index(_rawBits: 1376256)..<Swift.String.Index(_rawBits: 2555904))),
MyApp.ParagraphModel(text: "Paragraph 3: Banana", range: Range(Swift.String.Index(_rawBits: 2686976)..<Swift.String.Index(_rawBits: 3932160)))
]
或者,如果 ParagraphModel
中的 range
是 UITextRange
,你会这样做:
func getParagraphs() {
guard let textView = textView, let text = textView.text else { return }
models = []
text.enumerateSubstrings(in: text.startIndex..., options: .byParagraphs) { substring, range, _, stop in
let nsRange = NSRange(range, in: text)
if let substring = substring,
!substring.isEmpty,
let start = textView.position(from: textView.beginningOfDocument, offset: nsRange.location),
let end = textView.position(from: start, offset: nsRange.length),
let textRange = self.textView.textRange(from: start, to: end)
{
self.models.append(ParagraphModel(text: substring, range: textRange))
}
}
}
制作中:
[
MyApp.ParagraphModel(text: "Paragraph 1: Banana", range: <_UITextKitTextRange: 0x6000037983e0> (0, 19)F),
MyApp.ParagraphModel(text: "Paragraph 2: Apple", range: <_UITextKitTextRange: 0x600003798260> (21, 18)F),
MyApp.ParagraphModel(text: "Paragraph 3: Banana", range: <_UITextKitTextRange: 0x600003799f20> (41, 19)F)
]
我正在尝试使用我在下面键入的代码将段落拆分为一组组件。不过,我在使用 Ranges 元素时遇到了问题。我的问题是,如果用户多次键入一个字符串,它 returns 是第一次范围,而不是我想要的范围。
示例:
第 1 段:香蕉
第2款:苹果
第 3 段:香蕉
第 1 段和第 3 段都有第 1 段的范围。解决此问题的更明智的方法是什么?
lazy var models: [ParagraphModel] = []
struct ParagraphModel {
let text: String
let range: Range<String.Index>
}
func getParagraphs(){
let components = textView.text.components(separatedBy: "\n")
let models = components.compactMap { component -> ParagraphModel? in
if let range = textView.text.range(of: component) {
return ParagraphModel(text: component, range: range)
}
return nil
}
self.models = models
}
来自@Rob 的更新代码:
func getParagraphModel() {
guard let text = noteContents.text else { return }
var currentModels: [ParagraphModel] = []
text.enumerateSubstrings(in: text.startIndex..., options: .byParagraphs) { substring, range, _, stop in
if let substring = substring, !substring.isEmpty,
let textRange = self.noteContents.text.range(of: substring)
{
currentModels.append(ParagraphModel(text: substring, range: textRange))
}
}
self.models = currentModels
}
键入示例文本时,控制台的打印输出为:
[Project.ViewController.ParagraphModel(text: "Banana", range: Range(Swift.String.Index(_rawBits: 0)..<Swift.String.Index(_rawBits: 393216))), Project.ViewController.ParagraphModel(text: "Apple", range: Range(Swift.String.Index(_rawBits: 458752)..<Swift.String.Index(_rawBits: 786432))), Project.ViewController.ParagraphModel(text: "Banana", range: Range(Swift.String.Index(_rawBits: 0)..<Swift.String.Index(_rawBits: 393216)))]
你的代码片段中的问题是你总是会找到目标字符串的第一次出现。因此,当它搜索第三段“Banana”时,它将找到该字符串的第一次出现。
我建议 text.enumerateSubstrings(in:options:_:)
获取 text
内的段落范围。这样会枚举出子串及其各自的范围,避免了你自己去搜索文本,避免了同一个字符串出现多次可能出现的问题。
因此:
func getParagraphs() {
guard let textView = textView, let text = textView.text else { return }
models = []
text.enumerateSubstrings(in: text.startIndex..., options: .byParagraphs) { substring, range, _, _ in
guard let substring = substring, !substring.isEmpty else {
return
}
self.models.append(ParagraphModel(text: substring, range: range))
}
}
产生:
[
MyApp.ParagraphModel(text: "Paragraph 1: Banana", range: Range(Swift.String.Index(_rawBits: 0)..<Swift.String.Index(_rawBits: 1245184))),
MyApp.ParagraphModel(text: "Paragraph 2: Apple", range: Range(Swift.String.Index(_rawBits: 1376256)..<Swift.String.Index(_rawBits: 2555904))),
MyApp.ParagraphModel(text: "Paragraph 3: Banana", range: Range(Swift.String.Index(_rawBits: 2686976)..<Swift.String.Index(_rawBits: 3932160)))
]
或者,如果 ParagraphModel
中的 range
是 UITextRange
,你会这样做:
func getParagraphs() {
guard let textView = textView, let text = textView.text else { return }
models = []
text.enumerateSubstrings(in: text.startIndex..., options: .byParagraphs) { substring, range, _, stop in
let nsRange = NSRange(range, in: text)
if let substring = substring,
!substring.isEmpty,
let start = textView.position(from: textView.beginningOfDocument, offset: nsRange.location),
let end = textView.position(from: start, offset: nsRange.length),
let textRange = self.textView.textRange(from: start, to: end)
{
self.models.append(ParagraphModel(text: substring, range: textRange))
}
}
}
制作中:
[
MyApp.ParagraphModel(text: "Paragraph 1: Banana", range: <_UITextKitTextRange: 0x6000037983e0> (0, 19)F),
MyApp.ParagraphModel(text: "Paragraph 2: Apple", range: <_UITextKitTextRange: 0x600003798260> (21, 18)F),
MyApp.ParagraphModel(text: "Paragraph 3: Banana", range: <_UITextKitTextRange: 0x600003799f20> (41, 19)F)
]