Label設置行間距

Label設置行間距

內容摘要

  • UILabel顯示多行文本
  • UILabel設置行間距
  • 解決單行文本 & 多行文本顯示的問題

場景描述

  • 眾所周知,UILabel顯示多行的話,默認行間距為0,但實際開發中,如果顯示多行文本,一般情況下會有一定的行間距。如果想動態調整行間距,則需要賦值富文本屬性(而不是文本屬性

問題分析

Label顯示多行文本

  • label默認情況下,只會顯示單行文本,主要是因為它的numberOfLines屬性值是1;如果要顯示多行,把這個屬性值改成0即可。
self.lblResult.numberOfLines = 0;
  • 默認情況下,會顯示成這樣:


    Label設置行間距_多行0間距.png
  • 如果想添加行間距,你可能會這樣做:
    • 寫一個string轉換成AttributedString的方法(或者給字符串增加一個分類)
    -(NSAttributedString *)getAttributedStringWithString:(NSString *)string lineSpace:(CGFloat)lineSpace {
        NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
        paragraphStyle.lineSpacing = lineSpace; // 調整行間距
        NSRange range = NSMakeRange(0, [string length]);
        [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
        return attributedString;
    

}
* 賦值富文本屬性 objc
NSString string = @"眾所周知,UILabel顯示多行的話,默認行間距為0,但實際開發中,如果顯示多行文本,一般情況下會有一定的行間距。如果想動態調整行間距,則需要賦值富文本屬性*(而不是文本屬性)";
// 5:行間距
self.lblResult.attributedText = [self getAttributedStringWithString:string lineSpace:5];
```

  • 結果如下圖:


    Label設置行間距_多行5間距.png

    =============== 華麗的分割線 ===============
    </br>

問題:以上方法顯示多行文本貌似沒有問題,但如果文本只有一行呢?

Label顯示單行文本

  • 顯示單行中文:
NSString *string = @"文本只有一行會顯示什么樣?";
self.lblResult.attributedText = [self getAttributedStringWithString:string lineSpace:5];
Label設置行間距_單行中文5間距.png
  • 顯示單行英文:
NSString *string = @"good good study day day up";
self.lblResult.attributedText = [self getAttributedStringWithString:string lineSpace:5];
Label設置行間距_單行英文5間距1.png
  • 通過比較發現,用同樣的方法,單行顯示中文 & 英文,效果不同,中文會多了一些空白!心中立馬有種蛋蛋的憂桑,一絲絲凄涼……

遇到問題之后

  • 查詢API-NSMutableParagraphStyle

// Indent:縮進
@property(NS_NONATOMIC_IOSONLY) CGFloat lineSpacing;
@property(NS_NONATOMIC_IOSONLY) CGFloat paragraphSpacing;
@property(NS_NONATOMIC_IOSONLY) NSTextAlignment alignment;
@property(NS_NONATOMIC_IOSONLY) CGFloat firstLineHeadIndent;
@property(NS_NONATOMIC_IOSONLY) CGFloat headIndent;
@property(NS_NONATOMIC_IOSONLY) CGFloat tailIndent;
@property(NS_NONATOMIC_IOSONLY) NSLineBreakMode lineBreakMode;
@property(NS_NONATOMIC_IOSONLY) CGFloat minimumLineHeight;
@property(NS_NONATOMIC_IOSONLY) CGFloat maximumLineHeight;
@property(NS_NONATOMIC_IOSONLY) NSWritingDirection baseWritingDirection;
@property(NS_NONATOMIC_IOSONLY) CGFloat lineHeightMultiple;
@property(NS_NONATOMIC_IOSONLY) CGFloat paragraphSpacingBefore;
@property(NS_NONATOMIC_IOSONLY) float hyphenationFactor;
```

  • 各種嘗試之后,問題還在那兒……
  • 想到富文本屬性,查詢NSAttributedString.h頭文件
    • 仿佛看到了勝利的曙光
    UIKIT_EXTERN NSString * const NSBaselineOffsetAttributeName NS_AVAILABLE(10_0, 7_0);      // NSNumber containing floating point value, in points; offset from baseline, default 0
    

嘗試解決問題

  • 重構getAttributedStringWithString方法
-(NSAttributedString *)getAttributedStringWithString:(NSString *)string lineSpace:(CGFloat)lineSpace baselineOffset:(CGFloat)baselineOffset {
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    paragraphStyle.lineSpacing = lineSpace; // 調整行間距
    NSRange range = NSMakeRange(0, [string length]);
    [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
    // 設置文本偏移量
    [attributedString addAttribute:NSBaselineOffsetAttributeName value:@(baselineOffset) range:range];
    return attributedString;
}
  • 于是單行文本顯示成這樣:


    Label設置行間距_單行中文5間距偏移量.png

    Label設置行間距_單行英文5間距偏移量.png
  • 那么多行呢?


    Label設置行間距_多行中文5間距偏移量.png

    Label設置行間距_多行英文5間距偏移量.png

    我擦!

問題分析

  • 通過上面的示例分析,可以簡單的得到結論:
    • 未設置行間距和偏移量,什么問題都沒有,只是行與行之間顯示得比較緊促!
    • 只設置行間距,多行和單行英文情況下,顯示沒有問題,但單行中文顯示會有問題,底部會有空白!
    • 既設置行間距,也設置偏移的情況下,單行顯示沒有問題,但多行顯示有問題!

解決辦法

  • 多行情況下,不設置偏移!

那么問題來了,如何判斷label顯示幾行呢?

  • 筆者用比較笨的方法:計算某幾個固定字符的高度,然后再計算label文本的高度,如果后者大于前者,則為多行!
  • 示例代碼如下:
CGFloat lineSpace = 5;
CGFloat offset = -(1.0/3 * lineSpace) - 1.0/3;
CGFloat marginLeft = 20;
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = lineSpace; // 調整行間距
NSDictionary *attrs = @{
                            NSFontAttributeName : self.lblResult.font,
                            NSParagraphStyleAttributeName : paragraphStyle
                            };
// 計算一行文本的高度
CGFloat oneHeight = [@"測試Test" boundingRectWithSize:CGSizeMake(screenWidth-marginLeft*2, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size.height;
CGFloat rowHeight = [self.txtInputString.text boundingRectWithSize:CGSizeMake(screenWidth-marginLeft*2, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size.height;
// 如果超出一行,則offset=0;
offset = rowHeight > oneHeight ? 0 : offset;
self.lblResult.attributedText = [self getAttributedStringWithString:self.txtInputString.text lineSpace:lineSpace baselineOffset:offset];
  • OK,這樣貌似解決了上面的問題,但細心的你估計會發現一個問題:CGFloat offset = -(1.0/3 * lineSpace) - 1.0/3;這行代碼是什么意思?

關于 f(x) = -(1.0/3 * x) - 1.0/3

  • offset是通過窮舉法歸納總結出來的,也許不夠準確,但在項目中用起來挺好。
  • 根據文本內容,描點
// 描點
CGPoint points[15];
// CGPointMake(lineSpace, offset)
points[0] = CGPointMake(5, -2);
points[1] = CGPointMake(8, -3);
points[2] = CGPointMake(10, -3.5);
points[3] = CGPointMake(16, -6);
points[4] = CGPointMake(20, -7);
points[5] = CGPointMake(25, -9);
points[6] = CGPointMake(30, -11);
points[7] = CGPointMake(35, -11.5);
points[8] = CGPointMake(40, -13);
points[9] = CGPointMake(50, -15);
points[10] = CGPointMake(60, -18.5);
points[11] = CGPointMake(70, -23);
points[12] = CGPointMake(80, -26);
points[13] = CGPointMake(90, -29);
points[14] = CGPointMake(100, -32);
// 畫線
[self drawLine:points count:15];
  • 畫線
// 畫線
-(void)drawLine:(CGPoint[])points count:(NSInteger)count {
    CGMutablePathRef linePath = CGPathCreateMutable();
    CGPathAddLines(linePath, NULL, points, count);
    // 關聯layer和貝塞爾路徑
    self.linesLayer.path = linePath;
    CGPathRelease(linePath);
    // 創建Animation
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    animation.fromValue = @(0.0);
    animation.toValue = @(1.0);
    self.linesLayer.autoreverses = NO;
    animation.duration = 1.5f;
    // 設置layer的animation
    [self.linesLayer addAnimation:animation forKey:nil];
    self.linesLayer.strokeEnd = 1;
}
![Uploading Label設置行間距_歸納總結offset的算法_323780.png . . .]
Label設置行間距_歸納總結offset的算法.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容