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];
Shekhar Gupta
  • 6,206
  • 3
  • 30
  • 50
Ashish Agarwal
  • 14,555
  • 31
  • 86
  • 125

10 Answers10

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;
Community
  • 1
  • 1
Vaibhav Saran
  • 12,848
  • 3
  • 65
  • 75
  • 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. – jowie Mar 28 '14 at 11:32
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)

Bhavin Bhadani
  • 22,224
  • 10
  • 78
  • 108
Nick Lockwood
  • 40,865
  • 11
  • 112
  • 103
  • Nick. dont worry. i am using FXLabel in my project.. and its very easy so i post here.. – Mani Jul 05 '12 at 09:51
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

Community
  • 1
  • 1
jrc
  • 20,354
  • 10
  • 69
  • 64
  • This is the only answer that allowed me to correctly vertically align UILabel contents. I wish I could vote you up multiple times! – Don Miguel Sep 01 '14 at 09:58
  • This is the only solution that got my label to vertically align correctly. Thank you!!! – grabury Sep 09 '14 at 09:15
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.

rsc
  • 10,348
  • 5
  • 39
  • 36
Roshambo
  • 2,692
  • 1
  • 27
  • 24
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

Community
  • 1
  • 1
Cocoadelica
  • 3,006
  • 27
  • 30
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 !

Joffrey Outtier
  • 880
  • 10
  • 9
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.

Dan G
  • 21
  • 4
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!

Thomás Pereira
  • 9,589
  • 2
  • 31
  • 34
0

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

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

and add \n.. as suggested..

ingconti
  • 10,876
  • 3
  • 61
  • 48
-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"];
Fahim Parkar
  • 30,974
  • 45
  • 160
  • 276