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

Ios 将文本垂直对齐到UILabel内的顶部,ios,cocoa-touch,uikit,uilabel,text-alignment,Ios,Cocoa Touch,Uikit,Uilabel,Text Alignment,我有一个ui标签,有两行文本的空间。有时,当文本太短时,此文本将显示在标签的垂直中心 如何垂直对齐文本,使其始终位于ui标签的顶部 无法在UILabel上设置垂直对齐,但您可以通过更改标签的框架来获得相同的效果。我把我的标签做成了橙色,这样你就能清楚地看到发生了什么 以下是一种快速简便的方法: [myLabel sizeToFit]; 如果标签上的文字较长,会有多行,请将numberOfLines设置为0(这里的零表示无限行数) 较长版本 我会用代码做标签,这样你就能看到发生了

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

如何垂直对齐文本,使其始终位于
ui标签的顶部


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

以下是一种快速简便的方法:

    [myLabel sizeToFit];


如果标签上的文字较长,会有多行,请将
numberOfLines
设置为
0
(这里的零表示无限行数)


较长版本

我会用代码做标签,这样你就能看到发生了什么。您也可以在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(或略小于100)的标签(可能非常高)。在调整大小之前,可能需要将标签设置为所需的最小宽度

还有一些需要注意的事项:

是否尊重
lineBreakMode
,取决于它的设置方式
NSLineBreakByTruncatingTail
(默认值)在
sizeToFit
之后被忽略,其他两种截断模式(head和middle)也是如此<代码>NSLineBreakByClipping
也被忽略
NSLineBreakByCharWrapping
正常工作。帧宽度仍会缩小以适合最右边的字母


在评论中使用自动布局修复了NIB和故事板:

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)
    }

}
noOfLines=0
如果标签作为使用autolayout的ViewController的
视图
的子视图包含在nib或故事板中,则将
sizeToFit
调用放入
viewDidLoad
将不起作用,因为在调用
viewDidLoad
之后,自动布局会调整子视图的大小和位置,并将立即撤消
sizeToFit
调用的效果。但是,从
viewdilayoutsubviews
中调用
sizeToFit
将起作用


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

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)
    }

}
noOfLines=0
这将使用
NSString
方法
sizeWithFont:constrainedToSize:lineBreakMode:
计算适合字符串所需的帧高度,然后设置原点和宽度

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

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];
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
,但这不是必需的):

CGSize-fontSize=[self.text-sizeWithFont:self.font];
最终高度=fontSize.height*self.numberOfLines;
finalWidth=size.width//标签的预期宽度
CGSize theStringSize=[self.text size withfont:self.font constrainedToSize:CGSizeMake(finalWidth,finalHeight)lineBreakMode:self.lineBreakMode];
int newLinesToPad=(finalHeight-theStringSize.height)/fontSize.height;
对于(int i=0;i
就像上面的答案,但它不是很正确,或者很容易插入代码,所以我把它清理了一点。将此扩展名添加到它自己的.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

实现这一点的更快(更脏)的方法是将UILabel的换行模式设置为“Clip”,并添加固定数量的换行符

myLabel.lineBreakMode = UILineBreakModeClip;
myLabel.text = [displayString stringByAppendingString:"\n\n\n\n"];
这个解决方案不适用于所有人——特别是,如果您仍然希望在字符串末尾显示“…”,如果它超过了您正在显示的行数,您将需要使用较长的代码位之一——但在许多情况下,这将满足您的需要

  • 设置新文本:

    myLabel.text = @"Some Text"
    
  • 最大行数设置为0(自动):

  • 将标签的边框设置为最大尺寸:

    myLabel.frame = CGRectMake(20,20,200,800)
    
  • 调用
    sizeToFit
    以减小框架大小,使内容刚好适合:

    [myLabel sizeToFit]
    
  • 标签框架现在刚好足够高和宽以适合您的文本。左上角应保持不变。我只测试了左上对齐的文本。对于其他路线,可能必须在之后修改框架


    此外,我的标签还启用了文字换行功能。

    参考扩展解决方案:

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

    然后调用
    [yourLabel alignTop]
    [yourLabel alignBottom]我想要一个标签,它能够有多行,最小字体大小,并且在父视图中水平和垂直居中。我以编程方式将标签添加到视图中:

    - (void) customInit {
        // Setup label
        self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
        self.label.numberOfLines = 0;
        self.label.lineBreakMode = UILineBreakModeWordWrap;
        self.label.textAlignment = UITextAlignmentCenter;
    
        // Add the label as a subview
        self.autoresizesSubviews = YES;
        [self addSubview:self.label];
    }
    
    然后当我想更改标签的文本时

    - (void) updateDisplay:(NSString *)text {
        if (![text isEqualToString:self.label.text]) {
            // Calculate the font size to use (save to label's font)
            CGSize textConstrainedSize = CGSizeMake(self.frame.size.width, INT_MAX);
            self.label.font = [UIFont systemFontOfSize:TICKER_FONT_SIZE];
            CGSize textSize = [text sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
            while (textSize.height > self.frame.size.height && self.label.font.pointSize > TICKER_MINIMUM_FONT_SIZE) {
                self.label.font = [UIFont systemFontOfSize:self.label.font.pointSize-1];
                textSize = [ticker.blurb sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
            }
            // In cases where the frame is still too large (when we're exceeding minimum font size),
            // use the views size
            if (textSize.height > self.frame.size.height) {
                textSize = [text sizeWithFont:self.label.font constrainedToSize:self.frame.size];
            }
    
            // Draw 
            self.label.frame = CGRectMake(0, self.frame.size.height/2 - textSize.height/2, self.frame.size.width, textSize.height);
            self.label.text = text;
        }
        [self setNeedsDisplay];
    }
    
    希望这对别人有帮助!
    - (void) customInit {
        // Setup label
        self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
        self.label.numberOfLines = 0;
        self.label.lineBreakMode = UILineBreakModeWordWrap;
        self.label.textAlignment = UITextAlignmentCenter;
    
        // Add the label as a subview
        self.autoresizesSubviews = YES;
        [self addSubview:self.label];
    }
    
    - (void) updateDisplay:(NSString *)text {
        if (![text isEqualToString:self.label.text]) {
            // Calculate the font size to use (save to label's font)
            CGSize textConstrainedSize = CGSizeMake(self.frame.size.width, INT_MAX);
            self.label.font = [UIFont systemFontOfSize:TICKER_FONT_SIZE];
            CGSize textSize = [text sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
            while (textSize.height > self.frame.size.height && self.label.font.pointSize > TICKER_MINIMUM_FONT_SIZE) {
                self.label.font = [UIFont systemFontOfSize:self.label.font.pointSize-1];
                textSize = [ticker.blurb sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
            }
            // In cases where the frame is still too large (when we're exceeding minimum font size),
            // use the views size
            if (textSize.height > self.frame.size.height) {
                textSize = [text sizeWithFont:self.label.font constrainedToSize:self.frame.size];
            }
    
            // Draw 
            self.label.frame = CGRectMake(0, self.frame.size.height/2 - textSize.height/2, self.frame.size.width, textSize.height);
            self.label.text = text;
        }
        [self setNeedsDisplay];
    }
    
    @interface TopAlignedLabelContainer : UIView
    {
    }
    
    @end
    
    @implementation TopAlignedLabelContainer
    
    - (void)layoutSubviews
    {
        CGRect bounds = self.bounds;
    
        for (UILabel *label in [self subviews])
        {
            if ([label isKindOfClass:[UILabel class]])
            {
                CGSize fontSize = [label.text sizeWithFont:label.font];
    
                CGSize textSize = [label.text sizeWithFont:label.font
                                         constrainedToSize:bounds.size
                                             lineBreakMode:label.lineBreakMode];
    
                label.numberOfLines = textSize.height / fontSize.height;
    
                label.frame = CGRectMake(0, 0, textSize.width,
                     fontSize.height * label.numberOfLines);
            }
        }
    }
    
    @end
    
    // adjust the height of a multi-line label to make it align vertical with top + (void) alignLabelWithTop:(UILabel *)label { CGSize maxSize = CGSizeMake(label.frame.size.width, 999); label.adjustsFontSizeToFitWidth = NO; // get actual height CGSize actualSize = [label.text sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode]; CGRect rect = label.frame; rect.size.height = actualSize.height; label.frame = rect; } lblHello.text = @"Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!"; lblHello.numberOfLines = 5; [Utils alignLabelWithTop:lblHello];
    // 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
    
    #import <UIKit/UIKit.h>
    
    
    @interface KwLabelTopAlign : UILabel {
    
    }
    
    @end
    
    #import "KwLabelTopAlign.h"
    
    
    @implementation KwLabelTopAlign
    
    - (void)drawTextInRect:(CGRect)rect {
        int lineHeight = [@"IglL" sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, 9999.0f)].height;
        if(rect.size.height >= lineHeight) {
            int textHeight = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, rect.size.height)].height;
            int yMax = textHeight;
            if (self.numberOfLines > 0) {
                yMax = MIN(lineHeight*self.numberOfLines, yMax);    
            }
    
            [super drawTextInRect:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, yMax)];
        }
    }
    
    @end
    
    #import "KwLabelTopAlign.h"
    
    @implementation KwLabelTopAlign
    
    - (void)drawTextInRect:(CGRect)rect
    {
        CGFloat height = [self.text sizeWithFont:self.font
                                constrainedToSize:rect.size
                                    lineBreakMode:self.lineBreakMode].height;
        if (self.numberOfLines != 0) {
            height = MIN(height, self.font.lineHeight * self.numberOfLines);
        }
        rect.size.height = MIN(rect.size.height, height);
        [super drawTextInRect:rect];
    }
    
    @end
    
    
    @interface UITopAlignedLabel : UILabel
    
    @end
    
    
    @implementation UITopAlignedLabel
    
    #pragma mark Instance methods
    
    - (NSArray*)splitTextToLines:(NSUInteger)maxLines {
        float width = self.frame.size.width;
    
        NSArray* words = [self.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        NSMutableArray* lines = [NSMutableArray array];
    
        NSMutableString* buffer = [NSMutableString string];    
        NSMutableString* currentLine = [NSMutableString string];
    
        for (NSString* word in words) {
            if ([buffer length] > 0) {
                [buffer appendString:@" "];
            }
    
            [buffer appendString:word];
    
            if (maxLines > 0 && [lines count] == maxLines - 1) {
                [currentLine setString:buffer];
                continue;
            }
    
            float bufferWidth = [buffer sizeWithFont:self.font].width;
    
            if (bufferWidth < width) {
                [currentLine setString:buffer];
            }
            else {
                [lines addObject:[NSString stringWithString:currentLine]];
    
                [buffer setString:word];
                [currentLine setString:buffer];
            }
        }
    
        if ([currentLine length] > 0) {
            [lines addObject:[NSString stringWithString:currentLine]];
        }
    
        return lines;
    }
    
    - (void)drawRect:(CGRect)rect {
        if ([self.text length] == 0) {
            return;
        }
    
        CGContextRef context = UIGraphicsGetCurrentContext();
    
        CGContextSetFillColorWithColor(context, self.textColor.CGColor);
        CGContextSetShadowWithColor(context, self.shadowOffset, 0.0f, self.shadowColor.CGColor);
    
        NSArray* lines = [self splitTextToLines:self.numberOfLines];
        NSUInteger numLines = [lines count];
    
        CGSize size = self.frame.size;
        CGPoint origin = CGPointMake(0.0f, 0.0f);
    
        for (NSUInteger i = 0; i < numLines; i++) {
            NSString* line = [lines objectAtIndex:i];
    
            if (i == numLines - 1) {
                [line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeTailTruncation];            
            }
            else {
                [line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeClip];
            }
    
            origin.y += self.font.lineHeight;
    
            if (origin.y >= size.height) {
                return;
            }
        }
    }
    
    @end
    
    self.myLabel.text = @"Short Title";
    [self.myLabel sizeToFit];
    
    textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
    textField.userInteractionEnabled = NO; // Don't allow interaction
    
    @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
    
    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)
        }
    
    }
    
    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)
        }
    
    }
    
    - (void)drawTextInRect:(CGRect)rect
    {
        CGSize sizeThatFits = [self sizeThatFits:rect.size];
        rect.size.height = MIN(rect.size.height, sizeThatFits.height);
    
        [super drawTextInRect:rect];
    }
    
    - (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];
    }
    
    myLabel.text = [NSString stringWithFormat:@"%@\n\n\n\n\n\n\n\n\n",@"My label text string"];
    
    [labelObject setMinimumScaleFactor:0.5];
    [labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
    
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
    [label setText:@"Some text here"];
    [label setTranslatesAutoresizingMaskIntoConstraints:NO];
    [view addSubview:label];
    
    [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[label]|" options:0 metrics:nil views:@{@"label": label}]];
    [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[label]" options:0 metrics:nil views:@{@"label": label}]];
    
    @property (nonatomic) TTTAttributedLabel* label;
    <...>
    
    //view's or viewController's init method
    _label.verticalAlignment = TTTAttributedLabelVerticalAlignmentTop;
    
    noOfLines=0
    
    class TopAlignedLabel: UILabel {
      override func drawText(in rect: CGRect) {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        super.drawText(in: textRect)
      }
    }