在UILabel中垂直顶部对齐文本

UILabel文本垂直顶部对齐方法

在UILabel中垂直顶部对齐文本

技术背景

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

实现步骤

方法一:使用 sizeToFit

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

方法二:自定义 UILabel 子类

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

Objective-C 实现
@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 实现
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 实现
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 方法。

// 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

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

[myLabel alignTop];

方法五:使用 UITextField 替代

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

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 实现多行文本对齐

myLabel.numberOfLines = 0;
[myLabel sizeToFit];

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

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 后手动调整标签的宽度。

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 中。

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    [myLabel sizeToFit];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1010n111

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值