Как эффективно найти CGRects для видимых слов в UITextView?

Моя цель - пометить все видимые слова с ошибками в UITextView.

Неэффективный алгоритм состоит в том, чтобы использовать средство проверки орфографии, чтобы найти все диапазоны слов с ошибками в тексте, преобразовать их в объекты UITextRange, используя positionFromPosition: inDirection: offset и т. Д., А затем получить графические фрагменты с помощью метода UITextInput firstRectFromRange.

Таким образом, весь текст -> слова с ошибками-> коллекция NSRange -> коллекция UITextRange -> коллекция CGRect -> оценить на видимость, нарисовать видимые

Проблема заключается в том, что для этого требуется, чтобы весь текст был проверен, а все слова с ошибками были преобразованы в графические фрагменты.

Таким образом, я полагаю, что путь состоит в том, чтобы каким-то образом выяснить, какие части базового.text в UITextView видны в данный момент.

Таким образом, для диапазона видимого текста -> слова с ошибками-> коллекция NSRange -> коллекция UITextRange -> коллекция CGRect -> оценить видимость, нарисовать видимые

Код в ios - как найти видимый диапазон текста в UITextView? может работать как способ ограничения, какие части текста проверять, но все же требует, чтобы весь текст был измерен, что, я думаю, может быть довольно дорогостоящим.

Какие-либо предложения?

2 ответа

Решение

Поскольку UITextView это подкласс UIScrollView, его bounds Свойство отражает видимую часть его системы координат. Так что-то вроде этого должно работать:

- (NSRange)visibleRangeOfTextView:(UITextView *)textView {
    CGRect bounds = textView.bounds;
    UITextPosition *start = [textView characterRangeAtPoint:bounds.origin].start;
    UITextPosition *end = [textView characterRangeAtPoint:CGPointMake(CGRectGetMaxX(bounds), CGRectGetMaxY(bounds))].end;
    return NSMakeRange([textView offsetFromPosition:textView.beginningOfDocument toPosition:start],
        [textView offsetFromPosition:start toPosition:end]);
}

Это предполагает макет текста сверху вниз, слева направо. Если вы хотите, чтобы он работал для других направлений компоновки, вам придется работать усерднее.:)

Ответ Роба, написанный на Swift 4. Я добавил несколько проверок безопасности.

private func visibleRangeOfTextView(textView: UITextView) -> NSRange {
    let bounds = textView.bounds
    let origin = CGPoint(x: 10, y: 10)
    guard let startCharacterRange = textView.characterRange(at: origin) else {
        return NSRange(location: 0, length: 0)
    }

    let startPosition = startCharacterRange.start
    let endPoint = CGPoint(x: bounds.maxX,
                           y: bounds.maxY)
    guard let endCharacterRange = textView.characterRange(at: endPoint) else {
        return NSRange(location: 0, length: 0)
    }

    let endPosition = endCharacterRange.end

    let startIndex = textView.offset(from: textView.beginningOfDocument, to: startPosition)
    let endIndex = textView.offset(from: startPosition, to: endPosition)
    return NSRange(location: startIndex, length: endIndex)
}

Пример использования, вызываемый нажатием кнопки:

@IBAction func buttonTapped(sender: AnyObject) {
    let range = visibleRangeOfTextView(self.textView)

    // Note: "as NSString" won't work correctly with Emoji and stuff,
    // see also: http://stackru.com/a/24045156/1085556
    let nsText = self.textView.text as NSString
    let text = nsText.substringWithRange(range)
    NSLog("range: \(range), text = \(text)")        
}
Другие вопросы по тегам