By Stefan


2009-06-28 08:54:03 8 Comments

I have a UILabel with space for two lines of text. Sometimes, when the text is too short, this text is displayed in the vertical center of the label.

How do I vertically align the text to always be at the top of the UILabel?

image representing a UILabel with vertically-centered text

30 comments

@Baig 2017-03-28 11:27:31

Easiest approach using Storyboard:

Embed Label in StackView and set following two attributes of StackView in Attribute Inspector:

1- Axis to Horizontal,

2- Alignment to Top

enter image description here

@Daniel Beltrami 2018-08-02 18:39:23

For better results you can do this StackView > View > UILabel, because when you just embed an UILabel inside a StackView, the StackView resize to fit UILabel to minimum size

@Viktor Kucera 2018-09-10 07:01:47

Great idea to place UILabel into some UIView subclass (plain UIView is usually enough) and let that label grow in down direction. Thanks!

@Luiz Dias 2019-04-14 16:25:19

Awesome! It's also worth mentioning that you need to clear constraints on the UILabel and let the StackView do its job. Thanks!

@Sulthan 2011-09-28 11:46:03

This is an old solution, use the autolayout on iOS >= 6

My solution:

  1. Split lines by myself (ignoring label wrap settings)
  2. Draw lines by myself (ignoring label alignment)

@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

@Jakob Egger 2010-08-27 16:24:34

  1. Set the new text:

    myLabel.text = @"Some Text"
    
  2. Set the maximum number of lines to 0 (automatic):

    myLabel.numberOfLines = 0
    
  3. Set the frame of the label to the maximum size:

    myLabel.frame = CGRectMake(20,20,200,800)
    
  4. Call sizeToFit to reduce the frame size so the contents just fit:

    [myLabel sizeToFit]
    

The labels frame is now just high and wide enough to fit your text. The top left should be unchanged. I have tested this only with the top left-aligned text. For other alignments, you might have to modify the frame afterward.

Also, my label has word wrapping enabled.

@Jakob Egger 2010-09-17 08:50:14

Hey Ben, I just looked at my old code again, and updated my answer with the correct steps necessary.

@Dan Ellis 2011-07-14 21:34:07

This worked fine for me. I set the dimensions of my UILabel and changed numberOfLines to 0 in IB and then after setting the text called [myLabel sizeToFit].

@d2burke 2012-03-10 02:37:49

works perfectly for me, after setting the number of lines in Attr. Inspector

@Tom 2014-02-12 15:58:55

Does not work in case of number of lines more than 1

@Jose Manuel Abarca Rodríguez 2015-02-06 16:46:23

It doesn't work when you want a label of two lines, but the text requires more lines.

@jasongregori 2012-05-21 07:47:43

No muss, no fuss

@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

No muss, no Objective-c, no fuss but Swift 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)
    }

}

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

}

@l.vasilev 2017-03-14 11:55:23

The swift version worked for me without any side effects! Great work!

@rajat chauhan 2018-04-03 11:36:42

Probably the best answer here, works in all scenarios. sizeToFit doesn't work if label is in UITableviewCell. Good work.

@nevan king 2009-06-28 10:36:13

There's no way to set the vertical-align on a UILabel, but you can get the same effect by changing the label's frame. I've made my labels orange so you can see clearly what's happening.

Here's the quick and easy way to do this:

    [myLabel sizeToFit];

sizeToFit to squeeze a label


If you have a label with longer text that will make more than one line, set numberOfLines to 0 (zero here means an unlimited number of lines).

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Longer label text with sizeToFit


Longer Version

I'll make my label in code so that you can see what's going on. You can set up most of this in Interface Builder too. My setup is a View-Based App with a background image I made in Photoshop to show margins (20 points). The label is an attractive orange color so you can see what's going on with the dimensions.

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

Some limitations of using sizeToFit come into play with center- or right-aligned text. Here's what happens:

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

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

enter image description here

The label is still sized with a fixed top-left corner. You can save the original label's width in a variable and set it after sizeToFit, or give it a fixed width to counter these problems:

    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;

label alignment


Note that sizeToFit will respect your initial label's minimum width. If you start with a label 100 wide and call sizeToFit on it, it will give you back a (possibly very tall) label with 100 (or a little less) width. You might want to set your label to the minimum width you want before resizing.

Correct label alignment by resizing the frame width

Some other things to note:

Whether lineBreakMode is respected depends on how it's set. NSLineBreakByTruncatingTail (the default) is ignored after sizeToFit, as are the other two truncation modes (head and middle). NSLineBreakByClipping is also ignored. NSLineBreakByCharWrapping works as usual. The frame width is still narrowed to fit to the rightmost letter.


Mark Amery gave a fix for NIBs and Storyboards using Auto Layout in the comments:

If your label is included in a nib or storyboard as a subview of the view of a ViewController that uses autolayout, then putting your sizeToFit call into viewDidLoad won't work, because autolayout sizes and positions the subviews after viewDidLoad is called and will immediately undo the effects of your sizeToFit call. However, calling sizeToFit from within viewDidLayoutSubviews will work.


My Original Answer (for posterity/reference):

This uses the NSString method sizeWithFont:constrainedToSize:lineBreakMode: to calculate the frame height needed to fit a string, then sets the origin and width.

Resize the frame for the label using the text you want to insert. That way you can accommodate any number of lines.

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;

@hungbm06 2014-10-22 05:18:47

you need to remove autolayout

@AboulEinein 2014-10-29 14:10:52

Here's a simple way to solve the problem: stackoverflow.com/a/26632490/636559

@JK ABC 2014-10-31 01:37:23

If you use auto layout and storyboard, adding a constraint with option "Remove at build time" checked will help

@Albert Renshaw 2014-11-02 05:54:53

If you need this working with adjustsFontSizeToFitWidth then use sizeToFit then set the label's frame to 0, 0, theWidthYouWant, label.frame.size.height (which is the new height determined by sizeToFit) THEN apply adjustsFontSizeToFitWidth

@beetree 2015-01-23 01:45:28

This answer is unnecessarily complicated and it is using a "word around". Please see suggestion below of simply changing the vertical content hugging priority. Doesn't require any code...

@Jose Manuel Abarca Rodríguez 2015-02-06 16:38:30

Nevan's idea of using sizeToFit has a strange behavior when the label is part of a tablecell : when the cell is scrolled and becomes invisible, when it is scrolled back, the label's size has changed, and the text inside has wrong character wraping. So, don't use it for labels inside tablecells.

@Eugene Braginets 2015-07-14 14:08:01

Fo me (xCode6, autolayouts) setting HeightConstraing priority (249) less then ContentHugging priority (250) made the trick. Details are here: stackoverflow.com/a/19657784/1624912

@turingtested 2016-02-01 14:47:44

[myLabel sizeToFit] is no good in UITableViewCells, since labels here are reused and new texts may not fit.

@Alper 2016-05-02 10:45:13

I have no idea how you would call sizeToFit form viewDidLayoutSubviews since by then you would no longer have a handle on the UILabel.

@orafaelreis 2017-01-27 11:55:41

You still can use AutoLayout and constraints, but assure the [height] will not be fixed. For example: Set Left, Top and Right to equals 8px, but Bottom you can set "Greater Than or Equal" 8px. It'll say to iOS, let my sizeToFit decides the better height for my label.

@jowie 2017-09-04 14:04:57

You can just use a UITextView in most cases. stackoverflow.com/a/7661501/190657

@Mojtaba Hosseini 2019-10-09 19:23:55

- SwiftUI

In SwiftUI, all views are usually sizeToFit their content, but if you explicitly use frame modifier, you can use alignment parameter of the frame:

Text("Text")
    .background(Color.red)
    .frame(width: 100, height: 200, alignment: .top)

Also if you use any kind of Stack to arrange your views, all of them have a parameter similar to frame, called alignment:

ZStack(alignment: .top) {
    Color.red
    Text("Text")
        .background(Color.red)
}

@Ruben Marin 2019-01-31 13:00:50

It can be done with more flexibility setting a height constraint to the label in Interface Builder, binding it to the code with an IBOutlet and changing that height to show the text in a concrete vertical position. Example for center and bottom alignment:

labelHeightConstraint.constant = centerAlignment ? 30 : 15
layoutIfNeeded()

@Jack Tiong 2018-05-19 13:36:19

What I did in my app was to set the UILabel's line property to 0 as well as to create a bottom constraint of the UILabel and make sure it is being set to >= 0 as shown in the image below. enter image description here

@BennyTheNerd 2018-03-07 23:03:40

(As of March 7, 2018)

Swift 4

let maxFrameHeight = 75

myLabel = UILabel()
myLabel.frame = CGRect(x: 9, y: 9, width: 126, height: maxFrameHeight)
myLabel.text = "my labels text displayed"
myLabel.numberOfLines = 0

myLabel.sizeToFit()
let newFrameHeight = myLabel.frame.size.height
let safeNewHeight = min(newFrameHeight, maxFrameHeight)

myLabel.frame = CGRect(x: 9, y: 9, width: 126, height: safeNewHeight)

This will get the desired outcome, and it will make sure the new height of the UILabel doesn't pass a certain maximum height you desire for this label.

@Ivan Carosati 2018-03-09 05:16:37

It works well, but you have to set numberOfLines = 0!

@Ashish 2017-10-27 11:51:10

Here's a solution in Swift 4.0:

func viewDidLoad() {

    super.viewDidLoad()

    // 20 point top and left margin. Sized to leave 20 pt at right.
    let labelFrame = CGRect(x: 20, y: 20, width: 280, height: 150)
    let myLabel = UILabel(frame: labelFrame)
    myLabel.backgroundColor = UIColor.orange

    let labelText = "I am the very model of a modern Major-General, 
    I've information vegetable, animal, and mineral"
    myLabel.text = labelText

    // Tell the label to use an unlimited number of lines
    myLabel.numberOfLines = 0
    myLabel.sizeToFit()

    view.addSubview(myLabel) // add label
}

@Nuno Gonçalves 2017-06-26 07:26:19

Simple solution to this problem...

Place a UIStackView bellow the label.

Then use AutoLayout to set a vertical spacing of zero to the label and constraint the top to what was previously the bottom of the label. The label won't grow, the stackView will. What I like about this is that the stack view is non rendering. So it's not really wasting time rendering. Even though it makes AutoLayout calculations.

You probably will need to play with ContentHugging and Resistance though but that's simple as well.

Every now and then I google this problem and come back here. This time I had an ideia that I think is the easiest, and I don't think it's bad performance wise. I must say that I'm just using AutoLayout and don't really want to bother calculating frames. That's just... yuck.

@ma11hew28 2016-12-28 18:45:20

Use textRect(forBounds:limitedToNumberOfLines:).

class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        super.drawText(in: textRect)
    }
}

@Diogo Antunes 2017-10-04 16:33:27

You sir are a god!

@Michael Ochs 2018-01-28 21:31:55

This answer should get way more upvotes as it is by far the nicest and cleanest solution!

@seggy 2016-10-25 09:37:21

For Swift 3...

@IBDesignable class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        if let stringText = text {
            let stringTextAsNSString = stringText as NSString
            let labelStringSize = stringTextAsNSString.boundingRect(with: CGSize(width: self.frame.width,height: CGFloat.greatestFiniteMagnitude),
                                                                            options: NSStringDrawingOptions.usesLineFragmentOrigin,
                                                                            attributes: [NSFontAttributeName: font],
                                                                            context: nil).size
            super.drawText(in: CGRect(x:0,y: 0,width: self.frame.width, height:ceil(labelStringSize.height)))
        } else {
            super.drawText(in: rect)
        }
    }
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        layer.borderWidth = 1
        layer.borderColor = UIColor.black.cgColor
    }
}

@Elliott Davies 2016-06-14 06:24:34

For those of you with custom table cells attempting to fix this issue, add this to your custom table cell class:

Swift 2.2:

override func layoutSubviews() {
    labelName.sizeToFit()
}

This solved my issue.

@A.G 2016-03-29 06:16:56

Swift 2.0:

Make constant enum values in a empty Swift file.

//  AppRef.swift

import UIKit
import Foundation

enum UILabelTextPositions : String {

 case VERTICAL_ALIGNMENT_TOP = "VerticalAlignmentTop"
 case VERTICAL_ALIGNMENT_MIDDLE = "VerticalAlignmentMiddle"
 case VERTICAL_ALIGNMENT_BOTTOM = "VerticalAlignmentBottom"

}

Using UILabel Extension:

Make a empty Swift class and name it. Add the following

//  AppExtensions.swift

import Foundation
import UIKit

extension UILabel{ 
 func makeLabelTextPosition (sampleLabel :UILabel?, positionIdentifier : String) -> UILabel
 {
  let rect = sampleLabel!.textRectForBounds(bounds, limitedToNumberOfLines: 0)

  switch positionIdentifier
  {
  case "VerticalAlignmentTop":
   sampleLabel!.frame = CGRectMake(bounds.origin.x+5, bounds.origin.y, rect.size.width, rect.size.height)
   break;

  case "VerticalAlignmentMiddle":
   sampleLabel!.frame = CGRectMake(bounds.origin.x+5,bounds.origin.y + (bounds.size.height - rect.size.height) / 2,
    rect.size.width, rect.size.height);
   break;

  case "VerticalAlignmentBottom":
   sampleLabel!.frame = CGRectMake(bounds.origin.x+5, bounds.origin.y + (bounds.size.height - rect.size.height),rect.size.width, rect.size.height);
   break;

  default:
   sampleLabel!.frame = bounds;
   break;
  }
  return sampleLabel!

 }
}

Usage :

myMessageLabel.makeLabelTextPosition(messageLabel, positionIdentifier: UILabelTextPositions.VERTICAL_ALIGNMENT_TOP.rawValue)

@Dara Tith 2016-02-23 02:27:14

In swift,

let myLabel : UILabel!

To make your text of your Label to fit to screen and it's on the top

myLabel.sizeToFit()

To make your font of label to fit to the width of screen or specific width size.

myLabel.adjustsFontSizeToFitWidth = YES

and some textAlignment for label :

myLabel.textAlignment = .center

myLabel.textAlignment = .left

myLabel.textAlignment = .right

myLabel.textAlignment = .Natural

myLabel.textAlignment = .Justified

@velkoon 2018-10-06 00:26:02

Just a change of syntax from the old syntax of [myLabel sizeToFit]. Thank you!

@Code Roadie 2012-09-18 15:59:57

I've used a lot of the methods above, and just want to add a quick-and-dirty approach I've used:

myLabel.text = [NSString stringWithFormat:@"%@\n\n\n\n\n\n\n\n\n",@"My label text string"];

Make sure the number of newlines in the string will cause any text to fill the available vertical space, and set the UILabel to truncate any overflowing text.

Because sometimes good enough is good enough.

@Rambatino 2014-09-15 12:21:00

However given he variation in screen size heights of iOS devices, there would then need to be a variable number of '\n' to account for uilabel changing height (which is a possibility). Thus this method is not particularly useful long term.

@Code Roadie 2014-09-15 23:00:26

Note: "quick-and-dirty" not "perfect solution for all time." Just sayin'.

@Séraphin Hochart 2015-03-27 21:01:55

@CodeRoadie Quick and dirty did the trick for me, I had a couple of layout issues with the real answer. I could do it the "right way", but thank you for saving me some time!

@Code Roadie 2015-08-16 22:38:07

@dGambit please note: "quick and dirty"

@yano 2015-11-30 22:14:16

Something I learned recently when dynamically loading views with UITextView is that it can call dlopen in order to support text input. This can cause major lag on the UI thread, so this approach is much more performant!

@djdance 2016-01-14 21:37:00

good, but it ends all labels with "..."! Line Breaks set to Clip of course.

@Code Roadie 2016-01-14 22:02:04

@djdance Try changing your UILabel's Line Breaks attribute to "Truncate Tail"

@djdance 2016-01-15 15:04:16

@CodeRoadie, i tried it too, not work (Swift, both iOs9 and iOs7, with and without spaces between \n). Only work without "..." if "word wrap" set and spaces before \n. Thanks

@Rabindra Nath Nandi 2015-08-08 05:29:51

For Adaptive UI(iOS8 or after) , Vertical Alignment of UILabel is to be set from StoryBoard by Changing the properties noOfLines=0` and

Constraints

  1. Adjusting UILabel LefMargin, RightMargin and Top Margin Constraints.

  2. Change Content Compression Resistance Priority For Vertical=1000` So that Vertical>Horizontal .

Constraints of UILabel

Edited:

noOfLines=0

and the following constraints are enough to achieve the desired results.

enter image description here

@Gabriel 2016-12-01 16:55:10

This was awesome. Can you explain why the Vertical>Horizontal did the job? Thanks.

@jowie 2011-10-05 12:46:48

Just in case it's of any help to anyone, I had the same problem but was able to solve the issue simply by switching from using UILabel to using UITextView. I appreciate this isn't for everyone because the functionality is a bit different.

If you do switch to using UITextView, you can turn off all the Scroll View properties as well as User Interaction Enabled... This will force it to act more like a label.

enter image description here

@ninjaneer 2013-11-10 12:17:32

Not sure how this will affect the performance if many UILabels are converted to text views.

@Clifton Labrum 2014-04-29 00:05:47

All the other solutions on this thread didn't work for me in iOS 7 (for whatever reason). But simply using a UITextView gave me the desired result right away.

@Itai Spector 2015-09-11 07:19:54

It's a workaround, it still needs some adjustments to make it look authentic, but indeed this is the easiest none code solution that I saw, thumbs up.

@JCutting8 2016-01-17 00:29:10

I like this solution. Granted, there may be performance issues and such, for a simple label on a relatively simple view, this was perfect for what I needed (and I can't notice any performance impacts)

@Sira Lam 2018-02-27 04:05:27

A small drawback of this method is that it will introduce extra intrinsic padding to the text. A quick fix is to introduce a negative constraint.

@Daniel Beltrami 2018-08-01 19:43:32

This still fixing in one line my text

@Andrew Kirna 2019-05-14 18:42:57

Way easier than the other answers. Thanks.

@thesummersign 2012-03-12 18:37:06

As long as you are not doing any complex task, you can use UITextView instead of UILabels.

Disable the scroll.

If you want the text to be displayed completely just user sizeToFit and sizeThatFits: methods

@jjxtra 2013-09-27 15:35:55

FXLabel (on github) does this out of the box by setting label.contentMode to UIViewContentModeTop. This component is not made by me, but it is a component I use frequently and has tons of features, and seems to work well.

@Andrew Romanov 2014-08-28 08:50:55

You can use TTTAttributedLabel, it supports vertical alignment.

@property (nonatomic) TTTAttributedLabel* label;
<...>

//view's or viewController's init method
_label.verticalAlignment = TTTAttributedLabelVerticalAlignmentTop;

@Ramesh Muthe 2013-03-25 06:26:57

This code helps you to make the text aligned to top and also to make the label height to be fixed the text content.

instruction to use the below code

isHeightToChange by default it is true makes the height of label to same as text content automatically. isHeightToChange if it false make the text aligned to top with out decreasing the height of the label.

#import "DynamicHeightLable.h"

@implementation DynamicHeightLable
@synthesize isHeightToChange;
- (id)initWithFrame:(CGRect)frame 
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // Initialization code.
        self.isHeightToChange=FALSE;//default

    }
    return self;
}
- (void)drawTextInRect:(CGRect)rect
{
    if(!isHeightToChange){
    CGSize maximumLabelSize = CGSizeMake(self.frame.size.width,2500);

    CGFloat height = [self.text sizeWithFont:self.font
                           constrainedToSize:maximumLabelSize
                               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];

}
- (void) layoutSubviews
{
    [super layoutSubviews];
       if(isHeightToChange){
     CGRect ltempFrame = self.frame;
    CGSize maximumLabelSize = CGSizeMake(self.frame.size.width,2500);

    CGFloat height = [self.text sizeWithFont:self.font
                           constrainedToSize:maximumLabelSize
                               lineBreakMode:self.lineBreakMode].height;
    if (self.numberOfLines != 0) {
        height = MIN(height, self.font.lineHeight * self.numberOfLines);
    }
    ltempFrame.size.height = MIN(ltempFrame.size.height, height);

    ltempFrame.size.height=ltempFrame.size.height;
    self.frame=ltempFrame;
       }
}
@end

@petehare 2014-04-29 01:21:15

I've found the answers on this question are now a bit out-of-date, so adding this for the auto layout fans out there.

Auto layout makes this issue pretty trivial. Assuming we're adding the label to UIView *view, the following code will accomplish this:

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

The label's height will be calculated automatically (using it's intrinsicContentSize) and the label will be positioned edge-to-edge horizontally, at the top of the view.

@barndog 2013-12-13 03:25:42

I know this is an old post but vertically aligning text is a HUGE problem (at least for me it is) and I figured that I should share this solution since I couldn't find one myself.

Using drawRect is a little expensive in my opinion. The proper way to get a UILabel to vertically align is to not use a UILabel. Use a UITextView (multiline UITextField) and observe the content property like so:

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

Basically what's happening here is we set the class we're in as the observer, looking at the contentSize property with an option of NSKeyValueObservingOptionNew so every time the content changes, -(void)observeValueForKeyPath:ofObject:change:context: will get called and then you can calculate an offset size to align the text appropriately.

I can't take credit for this, the original idea came from here. But, this solution doesn't work on iOS7. After trolling around SO for a few hours, I found this: iOS 7 vertical alignment fix. The key line there is contentheight = [tv sizeThatFits:CGSizeMake(tv.frame.size.width, FLT_MAX)].height;. For some reason in iOS 7, getting the contentSize height doesn't work but that fixes it. Neither of the two solutions worked on their own but after a little tinkering, I was able to synthesize together the above solution.

@phatmann 2013-09-05 16:38:17

If you are using autolayout, set the vertical contentHuggingPriority to 1000, either in code or IB. In IB you may then have to remove a height constraint by setting it's priority to 1 and then deleting it.

@David Greco 2012-10-16 16:38:02

I've struggled with this one for a long time and I wanted to share my solution.

This will give you a UILabel that will autoshrink text down to 0.5 scales and vertically center the text. These options are also available in Storyboard/IB.

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

@Ashok 2015-03-10 13:27:27

It worked as perfect among all the answers provided. Thanks @David Greco. Along with these properties, if we set adjustsFontSizeToFitWidth to YES then the text will fit into the frame without modifying the label's frame.

@mbo42 2013-07-23 11:52:05

yourLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;

@Walty Yeung 2010-01-30 06:32:33

I took a while to read the code, as well as the code in the introduced page, and found that they all try to modify the frame size of label, so that the default center vertical alignment would not appear.

However, in some cases we do want the label to occupy all those spaces, even if the label does have so much text (e.g. multiple rows with equal height).

Here, I used an alternative way to solve it, by simply pad newlines to the end of label (pls note that I actually inherited the UILabel, but it is not necessary):

CGSize fontSize = [self.text sizeWithFont:self.font];

finalHeight = fontSize.height * self.numberOfLines;
finalWidth = 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 "];
}

@atul awasthi 2012-05-16 06:47:31

In UILabel vertically text alignment is not possible. But, you can dynamically change the height of the label using sizeWithFont: method of NSString, and just set its x and y as you want.

You can use UITextField. It supports the contentVerticalAlignment peoperty as it is a subclass of UIControl. You have to set its userInteractionEnabled to NO to prevent user from typing text on it.

Related Questions

Sponsored Content

27 Answered Questions

[SOLVED] How do you add multi-line text to a UIButton?

20 Answered Questions

[SOLVED] How to set top-left alignment for UILabel for iOS application?

28 Answered Questions

[SOLVED] Evenly space multiple views within a container view

8 Answered Questions

[SOLVED] iOS: Multi-line UILabel in Auto Layout

9 Answered Questions

[SOLVED] Vertically align UILabel

  • 2012-06-29 10:16:12
  • Ashish Agarwal
  • 36644 View
  • 10 Score
  • 9 Answer
  • Tags:   ios xcode uilabel

13 Answered Questions

[SOLVED] Aligning text and image on UIButton with imageEdgeInsets and titleEdgeInsets

20 Answered Questions

19 Answered Questions

[SOLVED] How to add line break for UILabel?

7 Answered Questions

[SOLVED] Top aligned UILabel, make text stick to top of label view -ios

1 Answered Questions

[SOLVED] vertically center align text in the UIlabel

Sponsored Content