Получить информацию о линии из UITextView и NSLayoutManager

Чтобы поддерживать протокол UIAccessibilityReadingContent, мне нужен UITextView, чтобы ответить на мои вопросы о его строках. Это методы протокола, которые мне нужно реализовать:

  • accessibilityLineNumberForPoint: < - указав координату, верните номер строки
  • accessibilityContentForLineNumber: < - Возвращает текст данной строки
  • accessibilityFrameForLineNumber: < - заданный номер строки, вернуть его кадр
  • accessibilityPageContent < - весь текст. Что у меня есть.:)

Я полагаю, что NSLayoutManager может мне помочь, но я не настолько разбираюсь в этом. Я решил кое-что из этого (я думаю), но все же нуждаюсь в некоторой помощи.

У Apple есть пример кода (здесь), который может получить мне количество строк в текстовом представлении:

NSLayoutManager *layoutManager = [textView layoutManager];
unsigned numberOfLines, index, numberOfGlyphs =
        [layoutManager numberOfGlyphs];
NSRange lineRange;
for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){
    (void) [layoutManager lineFragmentRectForGlyphAtIndex:index
            effectiveRange:&lineRange];
    index = NSMaxRange(lineRange);
}

Я полагаю, что с lineRange выше я могу вычислить прямые прямые с помощью этого метода на NSLayoutManager:

- (NSRect)boundingRectForGlyphRange:(NSRange)glyphRange inTextContainer:(NSTextContainer *)container

И учитывая lineRanges, я должен уметь вычислять номер строки для точки используя (путем нахождения lineRange, который содержит индекс глифа:

- (NSUInteger)glyphIndexForPoint:(CGPoint)point inTextContainer:(NSTextContainer *)container fractionOfDistanceThroughGlyph:(CGFloat *)partialFraction

Так что же остается, как мне получить содержимое строки (как NSString), учитывая номер строки?

Ответ 1

думая в простом решении, вот небольшой код, который воспроизводит то, что вам нужно

- (void)analyse:(UITextView *)textView
{
    NSLayoutManager *layoutManager = [textView layoutManager];
    NSString *string = textView.text;
    unsigned numberOfLines, index, stringLength = [string length];

    NSMutableArray *ranges = [NSMutableArray new];
    NSMutableArray *frames = [NSMutableArray new];
    for (index = 0, numberOfLines = 0; index < stringLength; numberOfLines++)
    {
        NSRange tmprange;
        NSRange range = [string lineRangeForRange:NSMakeRange(index, 0)];
        CGRect rect = [layoutManager lineFragmentRectForGlyphAtIndex:index
                                           effectiveRange:&tmprange];

        [ranges addObject:[NSValue valueWithRange:range]];

        [frames addObject:[NSValue valueWithCGRect:rect]];
        index = NSMaxRange(tmpRange);
    }
    self.ranges = ranges;
    self.frames = frames;
    self.numberOfLines = numberOfLines;
}

Пожалуйста, посмотрите свойства:

self.ranges = ranges;
self.frames = frames;
self.numberOfLines = numberOfLines;

Для создания этих свойств в вашем классе может быть следующее:

@property (nonatomic) NSInteger numberOfLines;
@property (strong, nonatomic) NSArray *ranges;
@property (strong, nonatomic) NSArray *frames;

Я предлагаю вам добавить вызов анализа в следующий делегат:

- (void)textViewDidChange:(UITextView *)textView

Там вы можете, например, после анализа получить кадр 2-й строки, просто выполнив: self.frames [1]

Или получить текст второй строки, выполнив: [textView.text substringWithRange: [self.ranges [1] rangeValue]]

Например, вот так:

if (self.numberOfLines > 1)
{
    NSRange range = [self.ranges[1] rangeValue];
    NSLog(@"2nd line = %@", [textView.text substringWithRange:range]);
    NSLog(@"2nd line frame = %@", self.frames[1]);
}

Имея все кадры в self.frames Я думаю, что вы можете легко сделать другое, угадать номер строки, используя координату.