获取NSString的可见字符范围



我正在尝试获取UILabel中可见字符的范围。例如,如果我有文本

Lorem ipsum dolor sit amet,consetetur sadipscing elitr,sed diam nonomy eirmod tempor invdunt ut labore et dolore magna aliquyam erat,sed diam-voluptua。在vero eos et accusam et justo duo dolores et ea reum。这是一个很好的例子,没有海洋动物保护区。Lorem ipsum dolor sit amet,consetetur sadipscing elitr,sed diam nonomy eirmod tempor invdunt ut labore et dolore magna aliquyam erat,sed diam-voluptua。在vero eos et accusam et justo duo dolores et ea reum。这是一个很好的例子,没有海洋动物保护区。

并且我的UILabel仅设置为宽度和高度100100。目标C中是否有预定义的方法来仅获取可见字符的范围?

Lorem ipsum dolor sit amet,consetetur sadipscing elitr,sed diam nonomy eirmod tempor invidunt。。。

其他答案指出,对于文本的大小,可能存在近似值,但没有真正的方法来获得UILabel渲染的实际文本,因为它不会公开渲染过程的任何输出。

如果通过类似TTTAttributedLabel的方式渲染文本,则可以查看渲染过程内部,并注入一些代码以获取有关所渲染文本的信息。

例如,-[TTTAttributedLabel drawFramesetter:attributedString:textRange:inRect:context:]通过生成CTFrameRef来使用CoreText渲染实际文本。CTFrameRef的任务是布置文本,并报告哪些文本适合,哪些不适合。

忽略截断:

如果您不关心由于截断而丢失字符的事实,则可以调用CTFrameGetVisibleStringRange来获取显示的字符范围。这个功能背后的想法是,像多列文本布局这样的东西需要知道下一帧从哪里开始:

CFRange actuallyRenderedRange = CTFrameGetVisibleStringRange(frame);
NSString *actuallyRenderedText = [attributedString.string substringWithRange:NSMakeRange(actuallyRenderedRange.location, actuallyRenderedRange.length)];

考虑截断:

截断很复杂,对于CoreText来说是一个黑盒。对于未截断的行,标签可以只渲染CTFrameGetLines中的行。对于截断的行,它必须使用CTLineCreateTruncatedLine,指定截断线、截断点和截断标记(…)。但是,一旦有了这行,就没有直接的方法来获取截断的文本,因为它可能发生在行的开头、结尾或中间。看见https://stackoverflow.com/a/5672594/860000.

如果假设结束,则可以强制截断为其自己的字形运行,并获取倒数第二个文本运行的结束位置。如果你将其与删除的字符数进行比较,你可以得到新的字符串:

  1. 在截断字符串中添加一个伪属性:

    NSMutableDictionary *truncationTokenStringAttributes = [self.truncationTokenStringAttributes mutableCopy];
    if (!truncationTokenStringAttributes) {
        truncationTokenStringAttributes = [[attributedString attributesAtIndex:(NSUInteger)truncationAttributePosition effectiveRange:NULL] mutableCopy];
    }
    truncationTokenStringAttributes[@"DummyAttributeToForceNewRun"] = @1;
    
  2. 创建truncatedLine后,将倒数第二个行程结束位置与正常线路长度进行比较:

    CFArrayRef truncatedLineRuns = CTLineGetGlyphRuns(truncatedLine);
    CFIndex truncatedLineRunCount = CFArrayGetCount(truncatedLineRuns);
    CFIndex numberOfCharactersRemoved;
    CFIndex untruncatedLineLength = CTLineGetStringRange(line).length;
    if (truncatedLineRunCount > 1) {
        CTRunRef lastRealRun = CFArrayGetValueAtIndex(truncatedLineRuns, truncatedLineRunCount - 2);
        CFRange lastRunRange = CTRunGetStringRange(lastRealRun);
        numberOfCharactersRemoved = untruncatedLineLength - (lastRunRange.length + lastRunRange.location);
    } else {
        numberOfCharactersRemoved = untruncatedLineLength; // Everything was removed.
    }
    if (numberOfCharactersRemoved > 0) {
        actuallyRenderedText = [actuallyRenderedText substringToIndex:actuallyRenderedText.length - numberOfCharactersRemoved];
    }
    actuallyRenderedText = [actuallyRenderedText stringByAppendingString:truncationTokenString];
    

当函数完成时,actuallyRenderedText将包含所呈现的实际文本,包括截断符号。我已经为各种字符串尝试过了,结果是一致的。

最新更新