我们知道当UILabel
显示不下内容时,会自动截断处理,我们还可以用lineBreakMode
来修改截断方式。当我们限定只能截断某些字符时,系统提供的截断方式就不能满足了。
自定义截断字符时,就会遇到一个问题,固定宽度下能显示多少字符串?
使用场景
偶尔收到了微信的专属红包,发现当微信名或备注较长时,会将用户名截断显示。
微信专属红包,昵称较长时显示如下:
给ABCDEFGHI...的专属红包
ABCDEFGHI...已领取
实现逻辑分析
微信红包气泡宽度是相对固定的,所以红包状态的宽度也是固定的。当用户名过长时,要保证其他字符能完整显示,只能截断用户名。
//其他字符
给xxx的专属红包
总宽度 = 其他字符宽度 + 用户名宽度(可截断)
由上我们知道,算出其他字符的宽度后,就能得到用户名宽度。拿到用户名可显示的宽度后,怎么知道能显示多少内容呢?
实现流程
查资料发现可以用CTFrame获取可见字符串范围 CTFrameGetVisibleStringRange
CFRange CTFrameGetVisibleStringRange(CTFrameRef frame);
由上定义,我们还需要创建一个CTFrameRef
,需用到 CTFramesetterCreateFrame
CTFrameRef CTFramesetterCreateFrame(CTFramesetterRef framesetter, CFRange stringRange, CGPathRef path, CFDictionaryRef frameAttributes);
通过path
参数,我们可以传入宽度限制,即可得到固定宽度下的可见字符串范围。
上代码
最好写在NSString
的Category
中,需要导入CoreText
框架。
注意:其中path的高度需大于font.lineHeight
,小于2倍font.lineHeight
,当大于2倍font.lineHeight
时,将按照多行来计算可显示的内容。
#import "NSString+STExtension.h"
#import <CoreText/CoreText.h>
/// 获取固定宽度下的可见字符串
/// @param width 宽度
/// @param font 字体
/// @param showEllipsis 结尾是否展示省略号
- (NSString *)getVisibleStringWithWidth:(CGFloat)width font:(UIFont*)font showEllipsis:(bool)showEllipsis
{
if (width == 0) {
return @"";
}
NSMutableParagraphStyle *p = [[NSMutableParagraphStyle alloc] init];
p.lineBreakMode = NSLineBreakByCharWrapping;
NSAttributedString *namesAtt = [[NSAttributedString alloc] initWithString:self attributes:@{NSFontAttributeName:font, NSParagraphStyleAttributeName:p}];
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)namesAtt);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, width, font.lineHeight + 1.0)];
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, self.length), path.CGPath, NULL);
CFRange range = CTFrameGetVisibleStringRange(frame);
CFRelease(framesetter);
CFRelease(frame);
NSString *resultStr = [self substringWithRange:NSMakeRange(range.location, range.length)];
if (showEllipsis && resultStr.length < self.length && resultStr.length > 0) {
resultStr = [resultStr stringByReplacingCharactersInRange:NSMakeRange(resultStr.length - 1, 1) withString:@"..."];
}
return resultStr;
}
最后
感谢阅读,有问题欢迎讨论。