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

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


当前回答

对于那些因为标签中的文本不是垂直居中而阅读本文的人,请记住,某些字体类型的设计并不相同。例如,如果您创建一个zapfino大小为16的标签,您将看到文本没有完全垂直居中。

然而,使用helvetica会使文本垂直居中。

其他回答

创建UILabel的子类。就像一个魅力:

// TopLeftLabel.h

#import <Foundation/Foundation.h>

@interface TopLeftLabel : UILabel 
{
}

@end

// TopLeftLabel.m

#import "TopLeftLabel.h"

@implementation TopLeftLabel

- (id)initWithFrame:(CGRect)frame 
{
    return [super initWithFrame:frame];
}

- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines 
{
    CGRect textRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];    
    textRect.origin.y = bounds.origin.y;
    return textRect;
}

-(void)drawTextInRect:(CGRect)requestedRect 
{
    CGRect actualRect = [self textRectForBounds:requestedRect limitedToNumberOfLines:self.numberOfLines];
    [super drawTextInRect:actualRect];
}

@end

正如这里所讨论的。

我知道这是一篇旧文章,但垂直对齐文本是一个巨大的问题(至少对我来说是这样),我想我应该分享这个解决方案,因为我自己找不到。

在我看来,使用drawRect有点贵。使UILabel垂直对齐的正确方法是不使用UILabel。使用UITextView(多行UITextField)并观察内容属性,如下所示:

- (UITextView*)textView{
     if(!_textView){
        UIEdgeInsets insets = UIEdgeInsetsMake(0, 50, 0, 5);
        CGRect frame = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
        _textView = [[UITextView alloc]initWithFrame:UIEdgeInsetsInsetRect(frame, insets)];
        _textView.delegate = self;
        _textView.scrollEnabled = YES;
        _textView.bounces = YES;
        _textView.backgroundColor = [UIColor whiteColor];
        [_textView setUserInteractionEnabled:NO];
        [_textView addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
    }
    return _textView;
}

 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:    (NSDictionary *)change context:(void *)context {
UITextView *tv = object;

CGFloat height = [tv bounds].size.height;
CGFloat contentheight;

#ifdef __IPHONE_7_0
    contentheight = [tv sizeThatFits:CGSizeMake(tv.frame.size.width, FLT_MAX)].height;
#else
    contentheight = [tv contentSize].height;
#endif

    switch(self.verticalAlignment) {
        case VerticalAlignmentBottom:{
            CGFloat topCorrect = ([tv bounds].size.height - contentheight);
            topCorrect = (topCorrect <0.0 ? 0.0 : topCorrect);
            tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
        }
        break;
        case VerticalAlignmentMiddle:{
            CGFloat topCorrect = (height - contentheight * [tv zoomScale])/2.0;
            topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
            tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
        }
            break;
        case VerticalAlignmentTop:{
            tv.contentOffset = (CGPoint){.x = 0, .y = 0 };
        }
            break;
        default:
            break;
    }
}

基本上,这里发生的是我们将我们所处的类设置为观察者,使用NSKeyValueObservingOptionNew选项查看contentSize属性,因此每次内容更改时,都会调用-(void)observeValueForKeyPath:ofObject:change:context:,然后您可以计算偏移量大小以适当对齐文本。

我不能把这归功于此,最初的想法来自这里。但是,该解决方案在iOS7上不起作用。在SO周围转了几个小时后,我发现了这个:iOS 7垂直对齐修复。其中的关键行是contentheight=[tv sizeThatFits:CGSizeMake(tv.frame.size.width,FLT_MAX)].height;。由于某种原因,在iOS 7中,获取contentSize高度不起作用,但这可以解决问题。这两个解决方案都不能单独工作,但经过一点修改后,我能够将上述解决方案合成在一起。

yourLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;

无法在UILabel上设置垂直对齐,但可以通过更改标签的框架来获得相同的效果。我把我的标签做成橙色,这样你就能清楚地看到发生了什么。

以下是快速简便的方法:

    [myLabel sizeToFit];


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

    myLabel.numberOfLines = 0;
    [myLabel 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的一些限制会影响居中或右对齐的文本。以下是发生的情况:

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

    [myLabel setNumberOfLines:0];
    [myLabel 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在评论中使用Auto Layout修复了NIB和Storyboards:

如果标签包含在笔尖或情节提要中,作为使用自动布局的ViewController视图的子视图,那么将sizeToFit调用放入viewDidLoad将不起作用,因为在调用viewDidLoad后自动布局将调整子视图的大小和位置,并将立即撤消sizeToFit调用的效果。但是,从viewDidLayoutSubviews内调用sizeToFit将起作用。

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

这使用NSString方法sizeWithFont:consainedToSize: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;

子类UILabel并约束绘图矩形,如下所示:

- (void)drawTextInRect:(CGRect)rect
{
    CGSize sizeThatFits = [self sizeThatFits:rect.size];
    rect.size.height = MIN(rect.size.height, sizeThatFits.height);

    [super drawTextInRect:rect];
}

我尝试了涉及换行填充的解决方案,在某些情况下遇到了错误的行为。根据我的经验,如上所述约束绘图矩形比使用numberOfLines更容易。

P.S.你可以想象一下这样轻松地支持UIViewContentMode:

- (void)drawTextInRect:(CGRect)rect
{
    CGSize sizeThatFits = [self sizeThatFits:rect.size];

    if (self.contentMode == UIViewContentModeTop) {
        rect.size.height = MIN(rect.size.height, sizeThatFits.height);
    }
    else if (self.contentMode == UIViewContentModeBottom) {
        rect.origin.y = MAX(0, rect.size.height - sizeThatFits.height);
        rect.size.height = MIN(rect.size.height, sizeThatFits.height);
    }

    [super drawTextInRect:rect];
}