一、前言
继上次本文本有行间距,当时交互有另一个需求,需要在文本最后一行省略符号放中间,只省略到最后一行的中间。如下图需求。Label的自带LineBreakMode不支持如下的设置。便要自己处理,经过网上的资料参考和同事J学习探讨,这里记录一下解决方案与过程。
图1.png二、分析
文本只需要最后一行进行处理,因此取得能取得文本最后一行,并进行计算,当最后一行的文本超过中间,则截取字符到中间,并增加一个“...”字符串。
三、解决方案
核心方法为网上大神所写获得Label每行文本字符串数组的方法,对拿到的最后一行进行处理。处理方式还是要利用获取每行文本的方法,传入一个显示label宽度的一半label。这时计算出来的最后一行的点点点省略号,误差就在一个字符。
//获得Label每行的文本字符串数组
- (NSArray *)getLinesArrayOfStringInLabel:(UILabel *)label {
NSString *text = [label text];
UIFont *font = [label font];
CGRect rect = [label bounds];
CTFontRef myFont = CTFontCreateWithName((__bridge CFStringRef)([font fontName]), [font pointSize], NULL);
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)myFont range:NSMakeRange(0, attStr.length)];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,rect.size.width,100000));
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
NSMutableArray *linesArray = [[NSMutableArray alloc]init];
for (id line in lines) {
CTLineRef lineRef = (__bridge CTLineRef )line;
CFRange lineRange = CTLineGetStringRange(lineRef);
NSRange range = NSMakeRange(lineRange.location, lineRange.length);
NSString *lineString = [text substringWithRange:range];
[linesArray addObject:lineString];
}
return (NSArray *)linesArray;
}
再利用此方法处理最后一行文本:
//Label每行文本数组
NSArray *separatedLines = [NSString getSeparatedLinesFromLabel:self.label];
NSMutableString *limitedText = [NSMutableString string];
if ( separatedLines.count >= self.label.numberOfLines ) {//当超过最大行数
for (int i = 0 ; i < self.label.numberOfLines; i++) {
if ( i == self.label.numberOfLines - 1) {//处理最后一行文本
UILabel *lastLineLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, self.label.frame.size.width/2, MAXFLOAT)];
lastLineLabel.text = separatedLines[self.label.numberOfLines - 1];
NSArray *subSeparatedLines = [NSString getSeparatedLinesFromLabel:lastLineLabel];
NSString *lastLineText = [subSeparatedLines firstObject];
NSInteger lastLineTextCount = lastLineText.length;
[limitedText appendString:[NSString stringWithFormat:@"%@...",[lastLineText substringToIndex:lastLineTextCount]]];
} else {//非最后一行,则将文本进行存储
[limitedText appendString:separatedLines[i]];
}
}
} else {
[limitedText appendString:self.text];
}
self.label.text = limitedText;
四、封装与使用
写一个Label分类,对需要进行最后一行中间省略号的Label调用一下 setLineBreakByTruncatingLastLineMiddle 方法,同时需要设置一下最大行数numberOfLines。
#import "UILabel+QT.h"
#import <CoreText/CoreText.h>
@implementation UILabel (QT)
- (void)setLineBreakByTruncatingLastLineMiddle {
if ( self.numberOfLines <= 0 ) {
return;
}
NSArray *separatedLines = [self getSeparatedLinesArray];
NSMutableString *limitedText = [NSMutableString string];
if ( separatedLines.count >= self.numberOfLines ) {
for (int i = 0 ; i < self.numberOfLines; i++) {
if ( i == self.numberOfLines - 1) {
UILabel *lastLineLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width/2, MAXFLOAT)];
lastLineLabel.font = self.font;
lastLineLabel.text = separatedLines[self.numberOfLines - 1];
NSArray *subSeparatedLines = [lastLineLabel getSeparatedLinesArray];
NSString *lastLineText = [subSeparatedLines firstObject];
NSInteger lastLineTextCount = lastLineText.length;
[limitedText appendString:[NSString stringWithFormat:@"%@...",[lastLineText substringToIndex:lastLineTextCount]]];
} else {
[limitedText appendString:separatedLines[i]];
}
}
} else {
[limitedText appendString:self.text];
}
self.text = limitedText;
}
- (NSArray *)getSeparatedLinesArray {
NSString *text = [self text];
UIFont *font = [self font];
CGRect rect = [self frame];
CTFontRef myFont = CTFontCreateWithName((__bridge CFStringRef)([font fontName]), [font pointSize], NULL);
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)myFont range:NSMakeRange(0, attStr.length)];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,rect.size.width,100000));
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
NSMutableArray *linesArray = [[NSMutableArray alloc]init];
for (id line in lines) {
CTLineRef lineRef = (__bridge CTLineRef )line;
CFRange lineRange = CTLineGetStringRange(lineRef);
NSRange range = NSMakeRange(lineRange.location, lineRange.length);
NSString *lineString = [text substringWithRange:range];
[linesArray addObject:lineString];
}
return (NSArray *)linesArray;
}
@end
五、思考
系统可以直接Label的lineBreakMode,如果可以给系统lineBreakMode增加一个枚举类型NSLineBreakByTruncatingLastLineMiddle,那在使用的时候,直接设置一下就好了是该多方便。不知道这个想法的可行性,需要学习了解看看。如果后续有这样的解决方案,再来补充。
经过探讨,想给系统lineBreakMode增加一个枚举还是行不通的,毕竟系统的代码没有开源,其次,要是能修改也是自己能用。所以,遇到这种情况,可以写一个类方法,或者是给类增加一个属性,例如otherLineBreakMode,进行处理。
六、参考资料
http://stackoverflow.com/questions/34867231/issue-get-lines-array-of-string-inn-label
个人博客地址:https://casscqt.github.io
网友评论