我在iOS上将多行字符串分解为字边界。我的解决方案围绕NSLayoutManager的boundingRectForGlyphRange方法。它几乎可以工作,除了每个单词的矩形是右边几个像素。换句话说,NSLayoutManager似乎在每一行上添加了一个前导空格/缩进,我找不到任何方法来覆盖这种行为。
我尝试使用NSLayoutManager.usesFontLeading以及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];
}
屏幕截图:灰色矩形表示标签边界,红色矩形表示每个标签的文本rect,以及上面[boundingRectForGlyphRange:]方法计算的单词边界。请注意,计算出的单词边界偏离了几个像素。
我也对其他计算单词边界的方法持开放态度,但boundingRectForGlyphRange对我来说似乎很方便。
答案 0 :(得分:2)
要忽略左边距,请使用:
textContainer.lineFragmentPadding = 0;
答案 1 :(得分:0)
我无法强制NSLayoutManager省略左边距。如果有人知道如何让NSLayoutManager忽略左边距,请告诉我。
我的解决方法是使用Core Text代替。这更加困难和参与。我的解决方案不适合粘贴到单个代码摘录中,但如果您想要使用相同的路径,这应该会给您一个很好的参考:
- (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获取一行中单词的边界矩形。我把它考虑到CTLineLayout模块中,但要点就是这个('origin'变量指的是上面代码示例中计算的行原点):
CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f;
CGFloat width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
CGFloat top = origin.y - ascent;
CGFloat bottom = origin.y + descent;
CGRect result = CGRectMake(origin.x, top, width, bottom - top); // frame of entire line (for now)
CGFloat left = CTLineGetOffsetForStringIndex(line, stringRange.location, NULL);
CGFloat right = CTLineGetOffsetForStringIndex(line, NSMaxRange(stringRange), NULL);
result.origin.x = left;
result.size.width = right - left; // frame of target word in UIKit coordinates
上面是一个粗略的摘录 - 我考虑CTLine在初始化器中计算一次线的边界,然后在获取一个单词的框架时仅计算左+右端点。
呼!