Vertically align UILabel
Asked Answered
E

10

11

I am trying to vertically align the text in the UILabel view of my app. The problem is that I want the text to be vertically aligned to the top and the size of the label to be 280 x 150. I am only able to achieve one of these 2 things. If I remove the line

[myLabel sizeToFit];

then the alignment of the text is alright but the size is messed up. But if I add the above line, then the alignment is messed up but the size is alright. How do I fix this problem.

I've added the code below -

CGRect labelFrame = CGRectMake(22, 50, 280, 150);
UILabel *myLabel = [[UILabel alloc] initWithFrame:labelFrame];
[myLabel setText:finalRecipe];
[myLabel setBackgroundColor: [UIColor lightGrayColor]];
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
[self.view addSubview:myLabel];
Eggert answered 29/6, 2012 at 10:16 Comment(3)
possible duplicate of How to set top-left alignment for UILable for iOS application?Webworm
Have you tried using UIFont to change the font size?Stockdale
why didn't you check marked any of the given answers!Deactivate
T
13

I'm the author of FXLabel, and whilst I don't know Manish, I believe he was trying to help (if he was advertising for me, I certainly didn't ask him to, and I'm not paying him - sorry Manish!).

One of FXLabel's features is that it respects the UIContentMode property of the label, as set in Interface builder. This means you can set label.contentMode = UIViewContentModeTop; to align the text to the top of the label view (which doesn't work for a regular UILabel). The relevant example is here:

https://github.com/nicklockwood/FXLabel/tree/master/Examples/Text%20Alignment

FXLabel is a drop-in subclass of UILabel, so I think it's a pretty good solution for the question being posed. However if the poster would rather solve the problem without using a 3rd party library (which is understandable) then here is the code to do it:

CGRect labelFrame = CGRectMake(22, 50, 280, 150);
UILabel *myLabel = [[UILabel alloc] initWithFrame:labelFrame];
[myLabel setText:finalRecipe];
[myLabel setBackgroundColor: [UIColor lightGrayColor]];
[myLabel setNumberOfLines:0];

CGFloat fontSize = 0.0f;
labelFrame.size = [myLabel.text sizeWithFont:myLabel.font
                                minFontSize:myLabel.minimumFontSize
                             actualFontSize:&fontSize
                                   forWidth:labelFrame.width
                            lineBreakMode:myLabel.lineBreakMode];

myLabel.frame = labelFrame;
[self.view addSubview:myLabel];

Note:(This is untested, so apologies if there are any typos)

Tenney answered 1/7, 2012 at 0:17 Comment(1)
Nick. dont worry. i am using FXLabel in my project.. and its very easy so i post here..Lowbred
D
19

dont use UILabel for this. Use UIButton with UserInteractionEnabled = NO; and that has options to vertical or horizontal align text inside it.

Here you go:

Swift:

btnDetail.titleLabel?.numberOfLines = 5
btnDetail.titleLabel?.lineBreakMode = .byCharWrapping
btnDetail.contentVerticalAlignment = .top
btnDetail.contentHorizontalAlignment = .left
btnDetail.isUserInteractionEnabled = false
btnDetail.autoresizesSubviews = true
btnDetail.autoresizingMask = .flexibleWidth

Obj-C

[btnDetail.titleLabel setNumberOfLines:5];
[btnDetail.titleLabel setLineBreakMode:NSLineBreakByCharWrapping];
[btnDetail setContentVerticalAlignment:UIControlContentVerticalAlignmentTop];
[btnDetail setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[btnDetail setUserInteractionEnabled:NO];
btnDetail.autoresizesSubviews = YES;
btnDetail.autoresizingMask = UIViewAutoresizingFlexibleWidth;
Deactivate answered 28/3, 2013 at 8:23 Comment(1)
For me, the text still aligns to the bottom centre... I only have one line of text so I has to remove the flexible width and the number of lines.Libertine
T
13

I'm the author of FXLabel, and whilst I don't know Manish, I believe he was trying to help (if he was advertising for me, I certainly didn't ask him to, and I'm not paying him - sorry Manish!).

One of FXLabel's features is that it respects the UIContentMode property of the label, as set in Interface builder. This means you can set label.contentMode = UIViewContentModeTop; to align the text to the top of the label view (which doesn't work for a regular UILabel). The relevant example is here:

https://github.com/nicklockwood/FXLabel/tree/master/Examples/Text%20Alignment

FXLabel is a drop-in subclass of UILabel, so I think it's a pretty good solution for the question being posed. However if the poster would rather solve the problem without using a 3rd party library (which is understandable) then here is the code to do it:

CGRect labelFrame = CGRectMake(22, 50, 280, 150);
UILabel *myLabel = [[UILabel alloc] initWithFrame:labelFrame];
[myLabel setText:finalRecipe];
[myLabel setBackgroundColor: [UIColor lightGrayColor]];
[myLabel setNumberOfLines:0];

CGFloat fontSize = 0.0f;
labelFrame.size = [myLabel.text sizeWithFont:myLabel.font
                                minFontSize:myLabel.minimumFontSize
                             actualFontSize:&fontSize
                                   forWidth:labelFrame.width
                            lineBreakMode:myLabel.lineBreakMode];

myLabel.frame = labelFrame;
[self.view addSubview:myLabel];

Note:(This is untested, so apologies if there are any typos)

Tenney answered 1/7, 2012 at 0:17 Comment(1)
Nick. dont worry. i am using FXLabel in my project.. and its very easy so i post here..Lowbred
R
12

You'll need to subclass UILabel. Try this:

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

    [super drawTextInRect:rect];
}

As you've discovered, UILabel vertically centers text within its bounds. Here we ask -sizeThatFits for the text block size and uses it to constrain the drawing rectangle passed to UILabel, so that text is drawn from the top of the label bounds.

You could even support the ignored (sigh) contentMode property like this:

- (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];
}

There's a bunch of other solutions and discussion here: Vertically align text to top within a UILabel

Rossy answered 3/7, 2012 at 15:12 Comment(2)
This is the only answer that allowed me to correctly vertically align UILabel contents. I wish I could vote you up multiple times!Barred
This is the only solution that got my label to vertically align correctly. Thank you!!!Winkler
K
11

In Swift, JRC’s solution of subclassing UILabel, overriding drawTextInRect and conforming to UIViewContentMode would look like this:

class AlignableUILabel: UILabel {

    override func drawText(in rect: CGRect) {

        var newRect = CGRect(x: rect.origin.x,y: rect.origin.y,width: rect.width, height: rect.height)
        let fittingSize = sizeThatFits(rect.size)

        if contentMode == UIViewContentMode.top {
            newRect.size.height = min(newRect.size.height, fittingSize.height)
        } else if contentMode == UIViewContentMode.bottom {
            newRect.origin.y = max(0, newRect.size.height - fittingSize.height)
        }

        super.drawText(in: newRect)
    }

}

Implementing is simply a matter of:

yourLabel.contentMode = UIViewContentMode.top

For me, it worked like a charm.

Karlise answered 21/3, 2015 at 16:12 Comment(1)
Note: @rsc updated this to Swift 3.1 on July 9, 2017 👏Karlise
G
1

There's a way without subclassing, using a button or rolling your own.

set the number of lines to 0, then call sizeToFit.

[label setNumberOfLines:0];
[label sizeToFit];

More details here as i think someone else linked to: Vertically align text to top within a UILabel

Grandmother answered 22/8, 2013 at 14:33 Comment(0)
B
1

You can make it with a constraint on the interface builder.

  1. Put the number of lines that you want
  2. Create a constraint with an height enough bigger for 3 lines and in relation section put "Less Than or Equal".

Hope this help you !

Bungle answered 26/10, 2016 at 13:46 Comment(0)
A
1

In addition to the above recommending setting the number of lines to 0 and the line break to Word Wrap be sure to not specify a height constraint. New lines are automatically added as needed by the displayed text and the text is always vertically aligned to the top of the label.

Accordance answered 18/12, 2019 at 21:15 Comment(0)
A
0

I did it what you want by implementing a category:

.h

@interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
@end

.m

@implementation UILabel (VerticalAlign)

#pragma mark
#pragma mark - Align Methods

- (void)alignTop
{
    [self setFrame:[self newLabelFrame:UIControlContentVerticalAlignmentTop]];
}

- (void)alignBottom
{
    [self setFrame:[self newLabelFrame:UIControlContentVerticalAlignmentBottom]];
}

#pragma mark
#pragma mark - Helper Methods

- (CGRect)newLabelFrame:(UIControlContentVerticalAlignment)alignment
{
    CGRect labelRect = self.frame;

    NSStringDrawingOptions options = NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesFontLeading;

    CGRect textRect = [self.text boundingRectWithSize:CGSizeMake(227.f, CGFLOAT_MAX)
                                              options:options
                                           attributes:@{NSFontAttributeName:self.font}
                                              context:nil];

    CGFloat textOffset = labelRect.size.height - textRect.size.height;

    UIEdgeInsets contentInsets;
    if (alignment == UIControlContentVerticalAlignmentTop)
    {
        if (textOffset < (labelRect.size.height/2.f))
        {
            contentInsets = UIEdgeInsetsMake(-textOffset, 0.f, 0.f, 0.f);
        }
        else
        {
            contentInsets = UIEdgeInsetsMake(0.f, 0.f, textOffset, 0.f);
        }
    }
    else
    {
        if (textOffset < (labelRect.size.height/2.f))
        {
            contentInsets = UIEdgeInsetsMake(0.f, 0.f, -textOffset, 0.f);
        }
        else
        {
            contentInsets = UIEdgeInsetsMake(textOffset, 0.f, 0.f, 0.f);
        }
    }

    return UIEdgeInsetsInsetRect(labelRect, contentInsets);
}

@end

Remember that you need to set "NumberOfLines" to 0. This example works with multilines options!

After you implement above category, you can simply do:

[myLabel setText:finalRecipe];
[myLabel alignTop];

Cheers!

Agnesse answered 23/8, 2014 at 14:49 Comment(0)
A
0

nice hack... my two cents for swift 4.2/5:

self.optTextMessage.numberOfLines =  0
self.optTitle.lineBreakMode  = .byWordWrapping

and add \n.. as suggested..

Aslant answered 2/3, 2019 at 18:5 Comment(0)
U
-2

You can do it as follows.......

  1. Set your label's numberOfLines property 0 from IB

  2. Set your label's lineBreakMode as UILineBreakModeWordWrap (very very important)

now whatever you set on the label just append few @"\n" to it..... ex.-

[yourTextLabel setText:@"myLabel\n\n\n\n\n"];
Uzia answered 17/10, 2013 at 20:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.