Как эффективно найти 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)")
}