在UILabel中垂直顶部对齐文本

在UILabel中垂直顶部对齐文本

技术背景

在iOS开发中,UILabel 本身没有直接设置垂直对齐的属性,但在实际开发中,我们可能需要将文本垂直顶部对齐。本文将介绍多种实现 UILabel 文本垂直顶部对齐的方法。

实现步骤

方法一:使用 sizeToFit

  1. 单行文本:直接调用 sizeToFit 方法,该方法会调整标签的大小以适应其内容。
1
[myLabel sizeToFit];
  1. 多行文本:先将 numberOfLines 设置为 0(表示不限行数),再调用 sizeToFit 方法。
1
2
myLabel.numberOfLines = 0;
[myLabel sizeToFit];

方法二:自定义 UILabel 子类

创建一个继承自 UILabel 的子类,重写 drawTextInRect: 方法。

Objective-C 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface MFTopAlignedLabel : UILabel
@end

@implementation MFTopAlignedLabel
- (void)drawTextInRect:(CGRect) rect
{
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:self.text attributes:@{NSFontAttributeName:self.font}];
rect.size.height = [attributedText boundingRectWithSize:rect.size
options:NSStringDrawingUsesLineFragmentOrigin
context:nil].size.height;
if (self.numberOfLines != 0) {
rect.size.height = MIN(rect.size.height, self.numberOfLines * self.font.lineHeight);
}
[super drawTextInRect:rect];
}
@end

Swift 3 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class VerticalTopAlignLabel: UILabel {
override func drawText(in rect:CGRect) {
guard let labelText = text else { return super.drawText(in: rect) }

let attributedText = NSAttributedString(string: labelText, attributes: [NSFontAttributeName: font])
var newRect = rect
newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height

if numberOfLines != 0 {
newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
}

super.drawText(in: newRect)
}
}

Swift 4.2 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class VerticalTopAlignLabel: UILabel {
override func drawText(in rect:CGRect) {
guard let labelText = text else { return super.drawText(in: rect) }

let attributedText = NSAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font])
var newRect = rect
newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height

if numberOfLines != 0 {
newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
}

super.drawText(in: newRect)
}
}

方法三:使用 StackView

UILabel 嵌入到 StackView 中,并在属性检查器中设置 AxisHorizontalAlignmentTop

方法四:使用扩展方法

创建一个 UILabel 的扩展,添加 alignTopalignBottom 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// UILabel+VerticalAlign.h
#pragma mark VerticalAlign
@interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
@end

// UILabel+VerticalAlign.m
@implementation UILabel (VerticalAlign)
- (void)alignTop {
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<newLinesToPad; i++)
self.text = [self.text stringByAppendingString:@"\n "];
}

- (void)alignBottom {
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<newLinesToPad; i++)
self.text = [NSString stringWithFormat:@" \n%@",self.text];
}
@end

使用时,设置文本后调用相应方法:

1
[myLabel alignTop];

方法五:使用 UITextField 替代

如果不需要 UILabel 的交互性,可以使用 UITextField 替代,并设置垂直对齐方式。

1
2
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // 禁止交互

方法六:自适应 UI 约束设置

对于 iOS 8 及以后的版本,可以在 Storyboard 中设置 UILabel 的属性和约束来实现垂直对齐。

  1. 设置 noOfLines = 0
  2. 调整 UILabel 的左、右、上边界约束。
  3. Content Compression Resistance Priority For Vertical 设置为 1000,使垂直优先级高于水平优先级。

核心代码

上述实现步骤中已经包含了核心代码,这里再次强调一些关键代码片段。

使用 sizeToFit 实现多行文本对齐

1
2
myLabel.numberOfLines = 0;
[myLabel sizeToFit];

自定义 UILabel 子类实现垂直顶部对齐(Swift 4.2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class VerticalTopAlignLabel: UILabel {
override func drawText(in rect:CGRect) {
guard let labelText = text else { return super.drawText(in: rect) }

let attributedText = NSAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font])
var newRect = rect
newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height

if numberOfLines != 0 {
newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
}

super.drawText(in: newRect)
}
}

最佳实践

  • 简单场景:如果只是简单的单行或多行文本对齐,优先使用 sizeToFit 方法。
  • 复杂场景:对于需要自定义布局和样式的场景,建议使用自定义 UILabel 子类的方法。
  • Storyboard 场景:在使用 Storyboard 进行布局时,使用 StackView 或约束设置可以更方便地实现垂直对齐。

常见问题

使用 sizeToFit 时文本对齐问题

当文本为居中或右对齐时,使用 sizeToFit 可能会导致布局问题。可以在 sizeToFit 后手动调整标签的宽度。

1
2
3
4
5
6
7
8
myLabel.textAlignment = NSTextAlignmentCenter;
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];

CGRect myFrame = myLabel.frame;
// 调整框架宽度
myFrame = CGRectMake(myFrame.origin.x, myFrame.origin.y, 280, myFrame.size.height);
myLabel.frame = myFrame;

使用自动布局时 sizeToFit 无效

如果 UILabel 包含在使用自动布局的 ViewControllerview 中,将 sizeToFit 调用放在 viewDidLoad 中可能无效。可以将其放在 viewDidLayoutSubviews 中。

1
2
3
4
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[myLabel sizeToFit];
}

在UILabel中垂直顶部对齐文本
https://119291.xyz/posts/2025-05-16.vertically-align-text-to-top-within-a-uilabel/
作者
ww
发布于
2025年5月16日
许可协议