有個需求需要在 UILabel 的最后一行后面放個按鈕怪与,但是需要和 UILabel 內(nèi)對齊壁酬,所以最后一行能顯示的文字就需要進(jìn)行壓縮邓嘹。
場景是文字最多出現(xiàn)三行,如果文字遮擋按鈕(不管文字需要幾行顯示)擅笔,遮擋的文字及前面兩個字符就都進(jìn)行壓縮志衣,變成…
初始思路是直接取最后一行寬度屯援,然后計算多少個文字會擋,然后替換最后的幾個文字念脯。狞洋。
調(diào)研了幾個方法,對英文來說有個比較常見的思路是绿店,每次截取一段文本吉懊,以空格為分界線,然后不斷往里面加詞假勿,直到超出一行的寬度借嗽,把這一行文本加入到一個數(shù)組中,然后在這行文本末尾進(jìn)行切分转培。直到切完整段文本恶导。
效率比較低,而且不適合中文浸须。
參考鏈接:https://stackoverflow.com/questions/10193073/ios-determine-last-line-width-of-uilabel
采用的是這個鏈接里另一個方法惨寿,使用了 NSLayoutManager,計算出整個文本的每行及每個字符的位置删窒,然后通過取最后一個字符的 Index裂垦,使用 lineFragmentRectForGlyphAtIndex:effectiveRange:
方法即可取出該字符所在行的 rect,寬度即在其中肌索。
這個方法看起來有完整的 API 支持蕉拢,決定從這個角度入手。
從另一個回答中看到诚亚,提問者有個類似的需求企量,不過他們是截取第二行的最后幾個字符,不需要考慮只有一行的情況亡电,所以可以用 Magic Number 直接指定 UILabel 中某個 point 的值,然后取出這個 point 的字符 Index硅瞧,進(jìn)行替換即可份乒。
由于我們不是要取所有文本的最后一行,而是取三行內(nèi)的最后一行腕唧,所以這個 Index 就無法簡單的直接取出來了或辖。
解決辦法是使用 enumerateLineFragmentsForGlyphRange
來獲取三行內(nèi)最后一行的 rect,然后和需要截取的位置進(jìn)行比較枣接,如果會發(fā)生文字和按鈕的重疊颂暇,就取重疊點的字符 Index,往前倒推兩個Index但惶,然后把剩下的文字替換…就行了耳鸯。
代碼:
- (NSAttributedString *)truncatingMessage:(NSAttributedString *)message
forLabel:(UILabel *)label
truncatingTailWidth:(CGFloat)tailWidth {
CGSize labelSize = CGSizeMake(label.width, INFINITY);
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *container = [[NSTextContainer alloc] initWithSize:labelSize];
NSTextStorage *storage = [[NSTextStorage alloc] initWithAttributedString:message];
[layoutManager addTextContainer:container];
[storage addLayoutManager:layoutManager];
container.lineFragmentPadding = 0;
container.lineBreakMode = label.lineBreakMode;
container.maximumNumberOfLines = label.numberOfLines;
NSRange range;
[layoutManager glyphRangeForCharacterRange:NSMakeRange(0, message.length - 1) actualCharacterRange:&range];
__block NSUInteger i = 0;
__block CGRect lastUsedRect;
__block NSRange lastGlyphRange;
[layoutManager enumerateLineFragmentsForGlyphRange:range usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) {
if (i < container.maximumNumberOfLines) {
lastUsedRect = usedRect;
lastGlyphRange = glyphRange;
i += 1;
} else {
*stop = YES;
}
}];
NSAttributedString *stringToUse = message;
// 最后一行文字寬度會與按鈕重疊才需要處理
if (lastUsedRect.size.width > label.width - tailWidth) {
// 算出重疊位置
CGPoint ellipsisPoint = CGPointMake(label.width - tailWidth, lastUsedRect.origin.y + lastUsedRect.size.height / 2);
// 重疊位置的字符 Index
NSUInteger characterIndex = [layoutManager characterIndexForPoint:ellipsisPoint inTextContainer:container fractionOfDistanceBetweenInsertionPoints:nil];
// 如果往后退兩個字符就到了上一行湿蛔,就不進(jìn)行壓縮了, 這里的 location 指的是最后一行第一個字符的 Index
if (characterIndex - 2 > lastGlyphRange.location) {
NSMutableAttributedString *tempString = [[message attributedSubstringFromRange:NSMakeRange(0, characterIndex - 2)] mutableCopy];
NSDictionary *attributes = [message attributesAtIndex:0 effectiveRange:nil];
[tempString appendAttributedString:[[NSAttributedString alloc] initWithString:@"…" attributes:attributes]];
stringToUse = [tempString copy];
}
}
return stringToUse;
}