在UITextView中拆分段落并保存范围

时间:2019-06-29 00:10:13

标签: ios swift uitextview

我正尝试使用我在下面键入的代码将段落拆分为组件数组。我在使用范围元素时遇到了麻烦。我的问题是,如果用户多次键入一个字符串,它将返回第一次的范围,而不是我想要的范围。

示例:
第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)))]

1 个答案:

答案 0 :(得分:1)

您的代码段中的问题是,您总是要查找目标字符串的第一个匹配项。因此,当搜索第三段“香蕉”时,它将找到该字符串的第一个匹配项。

我建议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)))
]

或者,如果range中的ParagraphModelUITextRange,则可以:

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)
]