UILabel相关iOS DeveloperiOS

iOS Label最后一行中间截断显示缩略符

2016-10-30  本文已影响1780人  Cass__

一、前言

继上次本文本有行间距,当时交互有另一个需求,需要在文本最后一行省略符号放中间,只省略到最后一行的中间。如下图需求。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

上一篇下一篇

猜你喜欢

热点阅读