将文本垂直对齐到 UILabel 中的顶部

我有一个UILabel带有两行文本的空间。有时,当文本太短时,该文本会显示在标签的垂直中心。

如何垂直对齐文本以使其始终位于UILabel的顶部?

用垂直居中的文本表示UILabel的图像

答案

无法在UILabel上设置垂直对齐,但是您可以通过更改标签的框架来获得相同的效果。我将标签设为橙色,以便您可以清楚地看到发生了什么。

这是执行此操作的快速简便的方法:

[myLabel sizeToFit];

sizeToFit挤压标签


如果您有一个带有较长文本的标签,该标签将包含多行,请将numberOfLines设置为0 (此处为零表示行数不受限制)。

myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

带有sizeToFit的较长标签文本


较长的版本

我将在代码中添加标签,以便您了解发生了什么。您也可以在 Interface Builder 中设置大多数功能。我的设置是一个基于视图的应用程序,带有在 Photoshop 中制作的背景图像以显示边距(20 点)。标签是吸引人的橙色,因此您可以看到尺寸的变化。

- (void)viewDidLoad
{
    [super viewDidLoad];

    // 20 point top and left margin. Sized to leave 20 pt at right.
    CGRect labelFrame = CGRectMake(20, 20, 280, 150);
    UILabel *myLabel = [[UILabel alloc] initWithFrame:labelFrame];
    [myLabel setBackgroundColor:[UIColor orangeColor]];

    NSString *labelText = @"I am the very model of a modern Major-General, I've information vegetable, animal, and mineral";
    [myLabel setText:labelText];

    // Tell the label to use an unlimited number of lines
    [myLabel setNumberOfLines:0];
    [myLabel sizeToFit];

    [self.view addSubview:myLabel];
}

使用sizeToFit一些限制会影响sizeToFit对齐或右对齐的文本。这是发生了什么:

// myLabel.textAlignment = NSTextAlignmentRight;
    myLabel.textAlignment = NSTextAlignmentCenter;

    [myLabel setNumberOfLines:0];
    [myLabel sizeToFit];

在此处输入图片说明

标签的大小仍带有固定的左上角。您可以将原始标签的宽度保存在一个变量中,并在sizeToFit之后进行sizeToFit ,或者给它固定的宽度以解决以下问题:

myLabel.textAlignment = NSTextAlignmentCenter;

    [myLabel setNumberOfLines:0];
    [myLabel sizeToFit];

    CGRect myFrame = myLabel.frame;
    // Resize the frame's width to 280 (320 - margins)
    // width could also be myOriginalLabelFrame.size.width
    myFrame = CGRectMake(myFrame.origin.x, myFrame.origin.y, 280, myFrame.size.height);
    myLabel.frame = myFrame;

标签对齐


请注意, sizeToFit将遵守初始标签的最小宽度。如果您以宽度为 100 的标签开始,并在其上调用sizeToFit ,它将返回宽度为 100(或更小)的(可能非常高)标签。您可能需要在调整大小之前将标签设置为所需的最小宽度。

通过调整框架宽度来校正标签对齐

其他注意事项:

是否尊重lineBreakMode取决于其设置方式。 NSLineBreakByTruncatingTail (默认值)后,被忽略sizeToFit ,因为是其他两个截断模式(头和中间)。 NSLineBreakByClipping也将被忽略。 NSLineBreakByCharWrapping照常工作。框架宽度仍然变窄以适合最右边的字母。


马克 · 阿默里Mark Amery )在评论中使用 “自动布局” 修复了 NIB 和情节提要:

如果您的标签被包括在笔尖或故事板作为一个子视图view一个视图控制器的使用自动版式,然后把你的sizeToFit电话到viewDidLoad是行不通的,因为自动布局大小和位置后子视图viewDidLoad被调用,将立即撤销您的sizeToFit调用的效果。但是,调用sizeToFit从内部viewDidLayoutSubviews 工作。


我的原始答案(供后代参考):

这使用NSString方法sizeWithFont:constrainedToSize:lineBreakMode:来计算适合字符串的框架高度,然后设置原点和宽度。

使用您要插入的文本调整标签框架的大小。这样,您可以容纳任意数量的行。

CGSize maximumSize = CGSizeMake(300, 9999);
NSString *dateString = @"The date today is January 1st, 1999";
UIFont *dateFont = [UIFont fontWithName:@"Helvetica" size:14];
CGSize dateStringSize = [dateString sizeWithFont:dateFont 
        constrainedToSize:maximumSize 
        lineBreakMode:self.dateLabel.lineBreakMode];

CGRect dateFrame = CGRectMake(10, 10, 300, dateStringSize.height);

self.dateLabel.frame = dateFrame;
  1. 设置新文本:

    myLabel.text = @"Some Text"
  2. maximum numbermaximum number设置为 0(自动):

    myLabel.numberOfLines = 0
  3. 将标签框架设置为最大尺寸:

    myLabel.frame = CGRectMake(20,20,200,800)
  4. 调用sizeToFit减小帧大小,以便内容恰好适合:

    [myLabel sizeToFit]

现在,标签框架的高度和宽度正好足以适合您的文本。左上角应保持不变。我只用左上方的文本进行了测试。对于其他对齐方式,您可能之后必须修改框架。

另外,我的标签启用了自动换行。

参考扩展解决方案:

for(int i=1; i< newLinesToPad; i++) 
    self.text = [self.text stringByAppendingString:@"\n"];

应该替换为

for(int i=0; i<newLinesToPad; i++)
    self.text = [self.text stringByAppendingString:@"\n "];

每个添加的换行符都需要额外的空间,因为似乎忽略了 iPhone UILabels的尾随回车:(

同样,也应使用@" \n@%"代替"\n@%"来更新 alignBottom(对于循环初始化,也必须用 “for(int i = 0 ...”)代替。

以下扩展名对我有用:

// -- file: UILabel+VerticalAlign.h
#pragma mark VerticalAlign
@interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
@end

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

然后调用[yourLabel alignTop];[yourLabel alignBottom];在每次 yourLabel 文本分配之后。

以防万一,这对任何人都有帮助,我遇到了同样的问题,但是仅通过从使用UILabel切换到使用UITextView就能解决此问题。我很欣赏这并不适合每个人,因为功能有些不同。

如果确实切换为使用UITextView ,则可以关闭所有 “滚动视图” 属性以及 “启用用户交互” ... 这将迫使它的行为更像一个标签。

在此处输入图片说明

没有糊涂,没有大惊小怪

@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

没有混乱,没有 Objective-c,没有大惊小怪,但雨燕 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)
    }

}

斯威夫特 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)
    }

}

就像上面的答案一样,但这不是很正确,或者很容易拍成代码,所以我整理了一下。将此扩展名添加到其自己的. h 和. m 文件中,或仅粘贴在要使用它的实现上方:

#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;    //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];

要么

[myLabel alignBottom];

一种更快捷(更脏)的方法是将 UILabel 的换行模式设置为 “剪辑”,并添加固定数量的换行符。

myLabel.lineBreakMode = UILineBreakModeClip;
myLabel.text = [displayString stringByAppendingString:"\n\n\n\n"];

此解决方案并非对所有人都适用 - 特别是,如果您仍然希望在字符串末尾显示 “...”(如果超出显示的行数),则需要使用以下方法之一较长的代码 - 但是在很多情况下,这可以满足您的需求。

可以使用具有垂直对齐选项的UITextField代替UILabel

textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction

使用情节提要的最简单方法:

Label嵌入StackView 中,并在 Attribute Inspector 中设置StackView 的以下两个属性:

1- AxisHorizontal

2- Alignment Top

在此处输入图片说明

我已经为此苦苦挣扎很长时间了,我想分享自己的解决方案。

这将为您提供一个UILabel ,它将文本自动缩小至 0.5 比例并垂直居中。这些选项在 Storyboard / IB 中也可用。

[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];