Ios 在NSLayoutManager中使用BoundingRectfolyPhrange计算单词边界时,如何消除前导空格

Ios 在NSLayoutManager中使用BoundingRectfolyPhrange计算单词边界时,如何消除前导空格,ios,textkit,Ios,Textkit,我正在iOS上将多行字符串分解为单词边界。我的解决方案以NSLayoutManager的BoundingRectfolyPhrange方法为中心。它几乎可以正常工作,只是每个单词的rect在右边有几个像素。换句话说,NSLayoutManager似乎在每一行上添加了前导空格/缩进,我找不到任何方法来覆盖这种行为 我尝试使用NSLayoutManager.usesFuntleding以及NSParagraphStyle.headIndent,但没有任何结果: NSLayoutManager* l

我正在iOS上将多行字符串分解为单词边界。我的解决方案以NSLayoutManager的BoundingRectfolyPhrange方法为中心。它几乎可以正常工作,只是每个单词的rect在右边有几个像素。换句话说,NSLayoutManager似乎在每一行上添加了前导空格/缩进,我找不到任何方法来覆盖这种行为

我尝试使用NSLayoutManager.usesFuntleding以及NSParagraphStyle.headIndent,但没有任何结果:

 NSLayoutManager* layout = [NSLayoutManager new];
layout.usesFontLeading = NO;
NSMutableParagraphStyle* paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.headIndent = paragraphStyle.firstLineHeadIndent = 0;
NSTextStorage* textStorage = [[NSTextStorage alloc] initWithString:self attributes:@{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle}];
layout.textStorage = textStorage;
NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:size];
[layout addTextContainer:textContainer];

// compute bounding rect for each word range
for (NSValue* wordRangeValue in wordRanges)
{
    NSRange wordRange = [wordRangeValue rangeValue];
    NSRange wordGlyphRange = [layout glyphRangeForCharacterRange:wordRange actualCharacterRange:NULL];
    CGRect wordBounds = [layout boundingRectForGlyphRange:wordGlyphRange inTextContainer:textContainer];
}

屏幕截图:灰色矩形表示标签边界,红色矩形表示每个标签的文本矩形,并根据上面的[BoundingRectforGlypRange:]方法计算单词边界。请注意,计算出的单词边界被关闭了几个像素


我也愿意使用其他方法来计算单词边界,但BoundingRectfolyPhrange对于我来说似乎非常方便。

我无法强制NSLayoutManager忽略左边距。如果有人知道如何让NSLayoutManager忽略左边距,请告诉我

我的解决方法是使用核心文本。这要困难得多,涉及面也大得多。我的解决方案不适合粘贴到单个代码摘录中,但如果您想使用相同的方法,这将为您提供一个很好的参考:

- (NSArray*) coreTextLayoutsForCharacterRanges:(NSArray*)ranges withFont:(UIFont *)font constrainedToSize:(CGSize)size{

// initialization: make frame setter
NSMutableParagraphStyle* paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; // watch out - need to specify line wrapping to use multi line layout!
paragraphStyle.minimumLineHeight = font.lineHeight; // watch out - custom fonts do not compute properly without this!
NSAttributedString* attributedString = [[NSAttributedString alloc] initWithString:self attributes:@{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle}];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
CFRange wholeString = CFRangeMake(0, self.length);
CGRect bounds = CGRectMake(0, 0, size.width, size.height);
CGMutablePathRef boundsPath = CGPathCreateMutable();
CGPathAddRect(boundsPath, NULL, bounds);
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, wholeString, boundsPath, NULL);
CFRelease(boundsPath);

// extract lines
CFArrayRef lines = CTFrameGetLines(frame);
int lineCount = CFArrayGetCount(lines);
CGPoint lineOrigins[lineCount];
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins);

NSMutableArray* lineLayouts = [NSMutableArray arrayWithCapacity:lineCount];
CGFloat h = size.height;
for (int i = 0; i < lineCount; ++i){
    CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
    CGPoint lineOrigin = lineOrigins[i];  // in Core Graphics coordinates! let's convert.
    lineOrigin.y = h - lineOrigin.y;
    TextLayout* lineLayout = [[CTLineLayout alloc] initWithString:self line:line lineOrigin:lineOrigin];
    [lineLayouts addObject:lineLayout];
}

// got line layouts. now we iterate through the word ranges to find the appropriate line for each word and compute its layout using the corresponding CTLine.
上面是一个粗略的摘录-我在初始值设定项中计算了CTLine的一次边界,然后在获得单词框架时只计算左+右端点


要忽略左边距,请使用:

textContainer.lineFragmentPadding = 0;

我看不出有什么问题。文本的图形代码在哪里?这种情况可能正在改变。请注意,UITextView通常会在左侧添加边距,但我认为UILabel不会这样做。但是NSLayoutManager不知道它是在标签还是文本视图中绘制的。文本只是UITableViewCell中的普通UILabel。我覆盖-layoutSubviews(当前在单元格中,但完成后我会将其分解为一个label子类),快速检查标签是否有两行文本,然后如上图所示计算单词边界以修复有问题/不均匀的换行。快速更新:我使用核心文本计算单词边界,它不像TextKit那样缩进行(但使用起来要困难得多)。另外:灰色框表示标签的边界,在计算文本布局后在-layoutsubview内绘制。似乎TextKit假设的缩进/边距与TextView使用的缩进/边距类似?精明的读者可能会注意到,CtlineGettyPrographicBounds是获取线条框架的一种迂回方式。但CTLineGetBounds并没有带来准确的结果(我忘记了确切的原因)。我还没有尝试过CTLineGetImageBounds。如果你真的想要完全控制,核心文本通常是最好的选择。我发现更高级别的API有太多复杂的行为,而且文档记录得很差。
textContainer.lineFragmentPadding = 0;