在UILabel中垂直顶部对齐文本
技术背景
在iOS开发中,UILabel
本身没有直接设置垂直对齐的属性,但在实际开发中,我们可能需要将文本垂直顶部对齐。本文将介绍多种实现 UILabel
文本垂直顶部对齐的方法。
实现步骤
方法一:使用 sizeToFit
- 单行文本:直接调用
sizeToFit
方法,该方法会调整标签的大小以适应其内容。
- 多行文本:先将
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
中,并在属性检查器中设置 Axis
为 Horizontal
,Alignment
为 Top
。
方法四:使用扩展方法
创建一个 UILabel
的扩展,添加 alignTop
和 alignBottom
方法。
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
| #pragma mark VerticalAlign @interface UILabel (VerticalAlign) - (void)alignTop; - (void)alignBottom; @end
@implementation UILabel (VerticalAlign) - (void)alignTop { CGSize fontSize = [self.text sizeWithFont:self.font]; double finalHeight = fontSize.height * self.numberOfLines; double finalWidth = self.frame.size.width; 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; 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
|
使用时,设置文本后调用相应方法:
方法五:使用 UITextField
替代
如果不需要 UILabel
的交互性,可以使用 UITextField
替代,并设置垂直对齐方式。
1 2
| textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; textField.userInteractionEnabled = NO;
|
方法六:自适应 UI 约束设置
对于 iOS 8 及以后的版本,可以在 Storyboard 中设置 UILabel
的属性和约束来实现垂直对齐。
- 设置
noOfLines = 0
。 - 调整
UILabel
的左、右、上边界约束。 - 将
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
包含在使用自动布局的 ViewController
的 view
中,将 sizeToFit
调用放在 viewDidLoad
中可能无效。可以将其放在 viewDidLayoutSubviews
中。
1 2 3 4
| - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [myLabel sizeToFit]; }
|