iOS富文本NSAttributedString垂直对齐

1.基础知识

1647711-1d32232991663b64.png

lineHeight = ascent + descent + leading

2.文字垂直对齐方式

屏幕快照 2018-03-12 下午1.48.53.png

iOS文本容器中都是基线对齐,所谓的基线对齐就是指无论中文字符,数字,英文字符,表情等它们在一行的时候,基线是在同一高度的。

NSMutableParagraphStyle *ParagraphStyle =  [[NSMutableParagraphStyle alloc] init];
    ParagraphStyle.lineHeightMultiple = 1;
    ParagraphStyle.lineSpacing = 0;
    ParagraphStyle.paragraphSpacing = 0;
    ParagraphStyle.paragraphSpacingBefore = 0;
    
    NSDictionary *dic1 = @{NSFontAttributeName:[UIFont systemFontOfSize:30],
                          NSForegroundColorAttributeName:[UIColor blackColor],
                          NSBackgroundColorAttributeName:[UIColor yellowColor],
                          NSBaselineOffsetAttributeName:@0,
                          NSParagraphStyleAttributeName:ParagraphStyle
                          };
    NSDictionary *dic2 = @{NSFontAttributeName:[UIFont systemFontOfSize:30],
                           NSForegroundColorAttributeName:[UIColor redColor],
                           NSBackgroundColorAttributeName:[UIColor yellowColor],
                           NSBaselineOffsetAttributeName:@0,
                           NSParagraphStyleAttributeName:ParagraphStyle
                           };
    
    NSDictionary *dic3 = @{NSFontAttributeName:[UIFont systemFontOfSize:30],
                           NSForegroundColorAttributeName:[UIColor blueColor],
                           NSBackgroundColorAttributeName:[UIColor yellowColor],
                           NSBaselineOffsetAttributeName:@0,
                           NSParagraphStyleAttributeName:ParagraphStyle
                           };
    NSMutableAttributedString *attributeStr1 =  [[NSMutableAttributedString alloc] initWithString:@"柯晓琪" attributes:dic1];
    NSAttributedString *attributeStr2 =  [[NSAttributedString alloc] initWithString:@" 1234567890" attributes:dic2];
    NSAttributedString *attributeStr3 =  [[NSAttributedString alloc] initWithString:@" 😆jqLvQ" attributes:dic3];
    [attributeStr1 appendAttributedString:attributeStr3];
    [attributeStr1 appendAttributedString:attributeStr2];
    
    NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
    attachment.image = [UIImage imageNamed:@"tsj"];
    attachment.bounds = CGRectMake(0,0,[UIFont systemFontOfSize:30].ascender,[UIFont systemFontOfSize:30].ascender);
    
    [attributeStr1 appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
    
    CGRect rect = [attributeStr1 boundingRectWithSize:CGSizeMake(600, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:NULL];
    
    NSLog(@"lineHeight : %f",[UIFont systemFontOfSize:30].lineHeight);
    NSLog(@"pointSize : %f",[UIFont systemFontOfSize:30].pointSize);
    NSLog(@"descender : %f",[UIFont systemFontOfSize:30].descender);
    NSLog(@"ascender : %f",[UIFont systemFontOfSize:30].ascender);
    NSLog(@"leading : %f",[UIFont systemFontOfSize:30].leading);
    
    NSLog(@"descender + ascender + leading = %f",[UIFont systemFontOfSize:30].ascender - [UIFont systemFontOfSize:30].descender + [UIFont systemFontOfSize:30].leading);
    NSLog(@"boundingRectWithSizeHeight : %f",rect.size.height);
    
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, 600, 100)];
    label.attributedText = attributeStr1;
    label.numberOfLines = 0;
    [self.view addSubview:label];


/*

2018-03-12 14:00:34.619455+0800 JQ_textView[13941:5472023] lineHeight : 35.800781
2018-03-12 14:00:34.619537+0800 JQ_textView[13941:5472023] pointSize : 30.000000
2018-03-12 14:00:34.619556+0800 JQ_textView[13941:5472023] descender : -7.236328
2018-03-12 14:00:34.619573+0800 JQ_textView[13941:5472023] ascender : 28.564453
2018-03-12 14:00:34.619596+0800 JQ_textView[13941:5472023] leading : 0.000000
2018-03-12 14:00:34.619614+0800 JQ_textView[13941:5472023] descender + ascender + leading = 35.800781
2018-03-12 14:00:34.619627+0800 JQ_textView[13941:5472023] boundingRectWithSizeHeight : 35.800781

*/

pointSize和UIFont的字体大小一致

ascender是基线之上的高度,通常为正

decender是基线之下的高度,通常为负

descender + ascender + leading计算出的高度在没有行间距和段间距距并且lineHeightMultiple=1的情况下和boundingRectWithSizeHeight计算出的高度一致。

3.字体大小不同文本垂直居中

屏幕快照 2018-03-12 下午2.35.15.png

所以需要设置NSBaselineOffsetAttributeName属性,可以达到目的,注意descender是负值。

//NSBaselineOffsetAttributeName:@(([UIFont systemFontOfSize:30].lineHeight - [UIFont systemFontOfSize:15].lineHeight)/2 + (([UIFont systemFontOfSize:30].descender - [UIFont systemFontOfSize:15].descender))),

NSDictionary *dic1 = @{NSFontAttributeName:[UIFont systemFontOfSize:15],
                          NSForegroundColorAttributeName:[UIColor blackColor],
                          NSBackgroundColorAttributeName:[UIColor yellowColor],
                          NSBaselineOffsetAttributeName:@(([UIFont systemFontOfSize:30].lineHeight - [UIFont systemFontOfSize:15].lineHeight)/2 + (([UIFont systemFontOfSize:30].descender - [UIFont systemFontOfSize:15].descender))),
                          NSParagraphStyleAttributeName:ParagraphStyle
                          };

屏幕快照 2018-03-12 下午2.38.17.png

4.NSTextAttachment对齐

    NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
    attachment.image = [UIImage imageNamed:@"tsj"];
    attachment.bounds = CGRectMake(0,0,[UIFont systemFontOfSize:30].ascender,[UIFont systemFontOfSize:30].ascender);

NSTextAttachment默认也是基线对齐,attachment.bounds的坐标原点Y轴是和基线持平,是coregraphics的坐标系。

如果将bounds高度设置为lineHeight,那么整个行高将会被拉大到lineHeight+decent的高度

NSTextAttachment.bounds = CGRectMake(0,0,[UIFont systemFontOfSize:30].lineHeight,[UIFont systemFontOfSize:30].lineHeight);
屏幕快照 2018-03-12 下午2.59.03.png

如果希望NSTextAttachment基线对齐

attachment.bounds = CGRectMake(0,0,[UIFont systemFontOfSize:30].ascender,[UIFont systemFontOfSize:30].ascender);

屏幕快照 2018-03-12 下午2.38.17.png

如果希望NSTextAttachment底部对齐

将bound.origin.y移动descender距离即可。

attachment.bounds = CGRectMake(0,[UIFont systemFontOfSize:30].descender,[UIFont systemFontOfSize:30].lineHeight,[UIFont systemFontOfSize:30].lineHeight);
屏幕快照 2018-03-12 下午3.02.05.png

推荐阅读更多精彩内容