ios获取UILabel每行显示的文字

最近在项目中遇到需要在UILabel上显示两种类型的数据的需求

  1. 第一种数据:
    最多显示2行且2行表示不完的情况下结尾以省略号表示

  2. 第二种数据:
    最多显示2行且以一种特殊的表现形式显示,举例:
    原始数据是:

成都是四川省省会,简称蓉,别称蓉城、锦城,西南地区唯一一个副省级市, [1] 特大城市,西部战区机关驻地, [2] 国务院确定的国家重要高新技术产业基地。

希望展示为:
[成都是四川省省会,简称蓉,别称蓉城、锦城,西南地区唯一一个副省级市...]xx的简介。
原始数据是:

NSDateFormatter类,简单来说就是OC提供好的格式化时间的类,可以将我们获取的时候格式化为我们自己想要展示的样子.这就需要了解一些字符的意义.

希望展示为:
[NSDateFormatter类,简单来说就是OC提供好的格式化时间的类,可以将我们...]xx的简介。
简单的归纳起来就是需要用特殊的文字对原始数据进行包装一下再展示出来:
[原始数据]xx的简介。
两行显示不完的情况下不是在整个句尾以省略号的形式表示而且原始数据的句尾以省略号的形式表示

  1. 除了以上的需求外还有些行间距等需求
    综合前面的需求,我打算用三方库TTTAttributedLabel来处理这些需求

首先分享一下我自己的思路吧:

  1. 第一种数据很好处理设置numberOfLines即可
  2. 第二种数据看见需求的时候我的第一想法:
    我能不能获取到数据在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:@"[%@]成都的简介。"];

效果如下

效果1

示例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:@"[%@]成都的简介。"];

效果如下

效果2

示例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:@""];

效果如下

效果3
总结

虽然说项目中的需求是满足了但是总觉得做的很繁琐,后面慢慢完善吧

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容