最近在项目中遇到需要在UILabel上显示两种类型的数据的需求
第一种数据:
最多显示2行且2行表示不完的情况下结尾以省略号表示第二种数据:
最多显示2行且以一种特殊的表现形式显示,举例:
原始数据是:
成都是四川省省会,简称蓉,别称蓉城、锦城,西南地区唯一一个副省级市, [1] 特大城市,西部战区机关驻地, [2] 国务院确定的国家重要高新技术产业基地。
希望展示为:
[成都是四川省省会,简称蓉,别称蓉城、锦城,西南地区唯一一个副省级市...]xx的简介。
原始数据是:
NSDateFormatter类,简单来说就是OC提供好的格式化时间的类,可以将我们获取的时候格式化为我们自己想要展示的样子.这就需要了解一些字符的意义.
希望展示为:
[NSDateFormatter类,简单来说就是OC提供好的格式化时间的类,可以将我们...]xx的简介。
简单的归纳起来就是需要用特殊的文字对原始数据进行包装一下再展示出来:
[原始数据]xx的简介。
两行显示不完的情况下不是在整个句尾以省略号的形式表示而且原始数据的句尾以省略号的形式表示
-
除了以上的需求外还有些行间距等需求
综合前面的需求,我打算用三方库TTTAttributedLabel来处理这些需求
首先分享一下我自己的思路吧:
- 第一种数据很好处理设置numberOfLines即可
-
第二种数据看见需求的时候我的第一想法:
我能不能获取到数据在label上一共会显示几行
我是否可以获取到每行显示的文字.
如果这些问题我都可以解决那就好办了.于是开始各种查资料最后都证实了自己的这些猜想是可以实现.然后我就开始干活了
首先我们对UILabel写一个延展,他的内部实现大致如下
#import <UIKit/UIKit.h>
#import <CoreText/CoreText.h>
NS_ASSUME_NONNULL_BEGIN
@interface UILabel (JSCUI)
// 获取一个数组 这个数据的元素由label的每行文字组成
- (NSArray *)getLinesArrayOfStringWidth:(CGFloat)width;
// 获取展示文字后Label的总行数
- (int)getLinesWithLabelWidth:(CGFloat)width;
@end
NS_ASSUME_NONNULL_END
#import "UILabel+JSCUI.h"
@implementation UILabel (JSCUI)
- (int)getLinesWithLabelWidth:(CGFloat)width {
return (int)[self getLinesArrayOfStringWidth:width].count;
}
- (NSArray *)getLinesArrayOfStringWidth:(CGFloat)width {
NSString *text = [self text];
UIFont *font = [self font];
if (text == nil) {
return nil;
}
CTFontRef myFont = CTFontCreateWithName(( CFStringRef)([font fontName]), [font pointSize], NULL);
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
[attStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, attStr.length)];
[attStr addAttribute:(NSString *)kCTFontAttributeName
value:(__bridge id)myFont
range:NSMakeRange(0, attStr.length)];
CFRelease(myFont);
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(( CFAttributedStringRef)attStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,width,100000));
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
NSArray *lines = ( 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];
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attStr,
lineRange,
kCTKernAttributeName,
(CFTypeRef)([NSNumber numberWithFloat:0.0]));
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attStr,
lineRange,
kCTKernAttributeName,
(CFTypeRef)([NSNumber numberWithInt:0.0]));
[linesArray addObject:lineString];
}
CGPathRelease(path);
CFRelease(frame);
CFRelease(frameSetter);
return (NSArray *)linesArray;
}
@end
准备工作完成后我们初始化一个lable
self.titleLabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(20, 100, [UIScreen mainScreen].bounds.size.width-40, 60)];
self.titleLabel.font = [UIFont systemFontOfSize:16.0];
self.titleLabel.numberOfLines = 2;
[self.view addSubview:self.titleLabel];
假设显示数据源是:
成都是四川省省会,简称蓉,别称蓉城、锦城,西南地区唯一一个副省级市, [1] 特大城市,西部战区机关驻地, [2] 国务院确定的国家重要高新技术产业基地、 [3] 商贸物流中心和综合交通枢纽,是西部地区重要的中心城市。 [4] 2017年,全市下辖20个区(市)县和高新区、天府新区成都直管区,面积14335平方公里,常住人口1604.5万人,GDP13889.39亿元。
核心代码
- (void)updateLabel:(UILabel *)txtLabel content: (NSString *)content frame:(CGRect)frame flagStr:(NSString *)flgStr {
//为了避免一步步处理数据的时候 在UI上出现各种奇怪的显示我们复制出一个和上面初始化的titleLabel一样的label出来在这上面处理数据带数据完全出来好之后再将数据赋值给titleLabel
UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.font = [UIFont systemFontOfSize:16.0];
label.text = content;
NSString *str = [[NSString alloc] init];
if ([self isBlankString:flgStr]) {
str = content;
} else {
//第一次尝试将数据源和固定文本结合
label.text = [NSString stringWithFormat:flgStr,content];
int lines = [self getLinesWithLabel:label];
//如果结合过后行数大于2
if (lines > 2) {
//那么先将label的text恢复成没有尝试结合之前的样子
label.text = content;
str = [self getTitle:content flagStr:flgStr label:label];
} else {
//如果结合过后的行数小于或等于2行直接就可以用这个处理后的数据
str = [NSString stringWithFormat:flgStr,content];
}
}
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:str];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
//此处的lineBreakMode设置一定要和UILabel延展里面获取每行内容是设置的一样,避免因为换行地方不同造成的计算误差
paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
paragraphStyle.lineSpacing = 5.0;
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [str length])];
[attributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:16.0] range:NSMakeRange(0, [str length])];
txtLabel.attributedText = attributedString;
}
- (int)getLinesWithLabel:(UILabel *)label {
return [label getLinesWithLabelWidth:CGRectGetWidth(label.frame)];
}
- (NSString *)getTitle:(NSString *)content flagStr:(NSString *)flagStr label:(UILabel *)label {
int lines = [self getLinesWithLabel:label];
//如果大于2行
if (lines >= 2) {
NSArray *contentArr = [label getLinesArrayOfStringWidth:CGRectGetWidth(label.frame)];
//将第一行和第二行的字符串重组
NSString *theFirstTwoLinesStr = [NSString stringWithFormat:@"%@%@…",contentArr[0],contentArr[1]];
int theFirstTwoLinesCount = (int)theFirstTwoLinesStr.length;
for (int i = 0; i < theFirstTwoLinesCount; i ++) {
//慢慢一步步截取之前我们由第一行和第二行组成的字符串的前theFirstTwoLinesCount-i-3)个字符串.3表示我们自己添加的“ …”的长度
NSString *tempStr = [theFirstTwoLinesStr substringWithRange:NSMakeRange(0, theFirstTwoLinesCount-i-3)];
NSString *str = [NSString stringWithFormat:@"%@…",tempStr];
str = [NSString stringWithFormat:flagStr,str];
label.text = str;
NSArray *tempArr = [label getLinesArrayOfStringWidth:CGRectGetWidth(label.frame)];
//直到我们截取到字符串和固定文字的组合刚刚好是2行就大功告成
if (tempArr.count <= 2) {
i = theFirstTwoLinesCount+1;
return str;
}
}
return content;
}
return content;
}
示例1
NSString *str = @"成都是四川省省会,简称蓉,别称蓉城、锦城,西南地区唯一一个副省级市, [1] 特大城市,西部战区机关驻地, [2] 国务院确定的国家重要高新技术产业基地、 [3] 商贸物流中心和综合交通枢纽,是西部地区重要的中心城市。 [4] 2017年,全市下辖20个区(市)县和高新区、天府新区成都直管区,面积14335平方公里,常住人口1604.5万人,GDP13889.39亿元。";
self.titleLabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(20,100,[UIScreen mainScreen].bounds.size.width-40,60)];
self.titleLabel.font = [UIFont systemFontOfSize:16.0];
self.titleLabel.numberOfLines = 2;
[self.view addSubview:self.titleLabel];
[self updateLabel:self.titleLabel content:str frame:self.titleLabel.frame flagStr:@"[%@]成都的简介。"];
效果如下
示例2
NSString *str = @"成都是四川省省会,简称蓉,别称蓉城、锦城";
self.titleLabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(20,100,[UIScreen mainScreen].bounds.size.width-40,60)];
self.titleLabel.font = [UIFont systemFontOfSize:16.0];
self.titleLabel.numberOfLines = 2;
[self.view addSubview:self.titleLabel];
[self updateLabel:self.titleLabel content:str frame:self.titleLabel.frame flagStr:@"[%@]成都的简介。"];
效果如下
示例3(不需要固定的文字)
NSString *str = @"成都是四川省省会,简称蓉,别称蓉城、锦城,西南地区唯一一个副省级市, [1] 特大城市,西部战区机关驻地, [2] 国务院确定的国家重要高新技术产业基地、 [3] 商贸物流中心和综合交通枢纽,是西部地区重要的中心城市。 [4] 2017年,全市下辖20个区(市)县和高新区、天府新区成都直管区,面积14335平方公里,常住人口1604.5万人,GDP13889.39亿元。";
self.titleLabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(20,100,[UIScreen mainScreen].bounds.size.width-40,60)];
self.titleLabel.font = [UIFont systemFontOfSize:16.0];
self.titleLabel.numberOfLines = 2;
[self.view addSubview:self.titleLabel];
[self updateLabel:self.titleLabel content:str frame:self.titleLabel.frame flagStr:@""];
效果如下
总结
虽然说项目中的需求是满足了但是总觉得做的很繁琐,后面慢慢完善吧