68

I am Copying the same Question asked Before Question. I have tried the solutions given and was not able to solve it since sizetofit was not effective when I use Autolayout.

first screenshot

The expected display is like below.

second screenshot

Community
  • 1
  • 1
MELWIN
  • 1,093
  • 4
  • 12
  • 19
  • Does the label need to be so big? You should be using the hugging and compression settings. – Wain Feb 19 '15 at 11:36
  • @Wain UILabel height is set to min 100px and max depends on the text inside. So when the text height is less than 100px, it is displayed on vertical center to the label. I need it to be on the top left. – MELWIN Feb 19 '15 at 11:44
  • If you're open to it TTTAttributedLabel is an open source component that lets you set vertical alignment – Daniel Galasko Feb 24 '15 at 13:35
  • 1
    Possible duplicate of [Vertically align text to top within a UILabel](http://stackoverflow.com/questions/1054558/vertically-align-text-to-top-within-a-uilabel) – Marius Constantinescu Nov 18 '16 at 13:11
  • 1
    Hey, just give constraint to your label as top,bottom, leading, trailing. Open size inspector and set bottom constraint is equal to greater than. Make sure you have set the no.of lines to 0. – Amit Kumar Jul 22 '17 at 12:24
  • @Amit Thanks a lot, pal! You save my day! This should be accepted as correct answer, as this a elegant way of doing this! – Cleversou Apr 29 '19 at 21:59

17 Answers17

135

Edit

In my original answer I was using the paragraph style of the label. Turns out that for multi-line labels this actually prevents the label from being multi-line. As a result I removed it from the calculation. See more about this in Github

For those of you more comfortable with using Open Source definitely look at TTTAttributedLabel where you can set the label's text alignment to TTTAttributedLabelVerticalAlignmentTop


The trick is to subclass UILabel and override drawTextInRect. Then enforce that the text is drawn at the origin of the label's bounds.

Here's a naive implementation that you can use right now:

Swift

@IBDesignable class TopAlignedLabel: UILabel {
    override func drawTextInRect(rect: CGRect) {
        if let stringText = text {
            let stringTextAsNSString = stringText as NSString
            var labelStringSize = stringTextAsNSString.boundingRectWithSize(CGSizeMake(CGRectGetWidth(self.frame), CGFloat.max),
                options: NSStringDrawingOptions.UsesLineFragmentOrigin,
                attributes: [NSFontAttributeName: font],
                context: nil).size
            super.drawTextInRect(CGRectMake(0, 0, CGRectGetWidth(self.frame), ceil(labelStringSize.height)))
        } else {
            super.drawTextInRect(rect)
        }
    }
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        layer.borderWidth = 1
        layer.borderColor = UIColor.blackColor().CGColor
    }
}

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

Objective-C

IB_DESIGNABLE
@interface TopAlignedLabel : UILabel

@end

@implementation TopAlignedLabel

- (void)drawTextInRect:(CGRect)rect {
    if (self.text) {
        CGSize labelStringSize = [self.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.frame), CGFLOAT_MAX)
                                                         options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
                                                      attributes:@{NSFontAttributeName:self.font}
                                                         context:nil].size;
        [super drawTextInRect:CGRectMake(0, 0, ceilf(CGRectGetWidth(self.frame)),ceilf(labelStringSize.height))];
    } else {
        [super drawTextInRect:rect];
    }
}

- (void)prepareForInterfaceBuilder {
        [super prepareForInterfaceBuilder];
        self.layer.borderWidth = 1;
        self.layer.borderColor = [UIColor blackColor].CGColor;
}

@end

Since I used IBDesignable you can add this label to a storyboard and watch it go, this is what it looks like for me

enter image description here

Community
  • 1
  • 1
Daniel Galasko
  • 23,617
  • 8
  • 77
  • 97
  • Can u help me to solve the same case when it comes in case of TTTAttributedLabel. – MELWIN Jun 03 '15 at 09:33
  • 1
    @MELWIN TTTAttributedLabel has a verticalAlignment property that you can set to .Top – Daniel Galasko Jun 03 '15 at 09:34
  • @daneil Thank u. Worked fine – MELWIN Jun 03 '15 at 09:45
  • 1
    @DanielGalasko if the text is longer than the width of the label it turns into ellipsis rather than going next line. I have already set `Lines: 0` in the XCode but it does not work. How to fix it? – MK Yung Jun 20 '15 at 04:37
  • @MKYung set numberOfLines to 0 and make sure you set the preferredMaxLayoutWidth to the width of the labels frame – Daniel Galasko Jun 20 '15 at 08:13
  • @DanielGalasko Thank you for the solution. I seem to have the same problem as MK (I also ensured that lines were set to 0, and `preferredMaxLayoutWidth = YES`. When testing using static text that's 2 lines, this solution works. However, when I have a long text, the "..." ellipses show up. Thanks for looking into it. – daspianist Jun 23 '15 at 19:18
  • @daspianist preferredMaxLayoutWidth is not a Boolean, perhaps that's your error? – Daniel Galasko Jun 23 '15 at 19:40
  • @DanielGalasko ah you're right, and thanks for your reply! I am using Storyboard (the `UILabel` appear inside a `UITableViewCell`), and changed the max width to the same width that the label appears inside my cell. However, the ellipses persist... – daspianist Jun 23 '15 at 19:44
  • Darn, maybe setup a github repo so we can get to the bottom of this :) @daspianist – Daniel Galasko Jun 23 '15 at 19:45
  • Thank you @DanielGalasko! Basic repo is here: https://github.com/zallanx/TopAlignedLabelTest – daspianist Jun 23 '15 at 20:00
  • 1
    @daspianist solved the problem and updated the SO post as well as submitted a PR on your Github. Thanks so much for the effort! – Daniel Galasko Jun 24 '15 at 08:47
  • @DanielGalasko Thank *you* for the effort and your excellent answer! Appreciate all your time. – daspianist Jun 24 '15 at 15:52
  • 2
    @DanielGalasko I found that using this code with text that was too big to fit the label's frame led to an offset appearing at the top. Fixed with `CGFloat height = MIN(labelStringSize.height, CGRectGetHeight(self.frame)); [super drawTextInRect:CGRectMake(0, 0, ceilf(CGRectGetWidth(self.frame)), height)];` – CharlesA Mar 17 '16 at 12:39
  • It works but I'm getting error in story board like this "Failed to render instance of TopAlignedLabel: dlopen.........................." – siva krishna Oct 17 '16 at 07:55
  • Although this seems to be working, lineBreakMode with TailTrailing doesn't work. Looks like this behavior is overridden by subclassing. What's the fix? – elquimista Nov 01 '16 at 16:20
  • As others have stated, it doesn't work if the text is truncated. e.g. Using `numberOfLines = 2` with `lineBreakMode = .byTruncatingTail` and the text doesn't fit. There is extra padding above the text. – David Pettigrew Jan 15 '19 at 16:29
47

If you're not restricted by having UILabel of fixed size, instead of aligning the text within a UILabel, simply use ≥ constraint on the given label to change the size of it.

enter image description here

It's the most elegant solution using Auto Layout. Don't forget to set numberOfLines to zero though.

Nikola Milicevic
  • 2,886
  • 3
  • 17
  • 17
33

You can use UITextView instead of UILabel:
Uncheck "Scrolling enabled"
Uncheck "Editable"
Uncheck "Selectable"
Set background color to ClearColor

arturdev
  • 10,884
  • 2
  • 39
  • 67
  • 3
    this works fine for me... didn't have to override a class or anything a uitextbox did it... thanks a lot for this! – MizAkita Mar 05 '16 at 18:48
  • 1
    Simple solution without using custom IBDesignables. Worked perfectly for me using in iOS 9.2 / Xcode 7.2.1 – Mark Barrasso Mar 10 '16 at 20:26
  • 1
    Also, in order for the text within the UITextView to align perfectly with another UILabel, I used this line of code: `self.textView.textContainer.lineFragmentPadding = 0;` – Mark Barrasso Mar 10 '16 at 20:45
  • Best solution so far. Works on iOS 11.2 / XCode 9.2 – Darkwonder Apr 04 '18 at 12:19
8

I had the same problem, and this is how I solved it. I just edited the Baseline under Attribute Inspector for the Label. Set it to "Align Centers".

Setting baseline to Align Centers

Oyvkva
  • 491
  • 6
  • 9
7

Instead, I changed the Bottom Space Constant to priority @250 and solved my problem. And my label has height constant with <= constant

Felipe FMMobile
  • 1,641
  • 20
  • 17
  • Easy solution! Worked for me too – Glenn Sønderskov May 09 '17 at 09:07
  • I'm wondering why would this work, what's the science behind? – Luis Pena May 07 '18 at 19:15
  • 1
    @LuisPena By default Vertical Content Hugging priority on views set in storyboard is 251, when layout engine sees bottom constraint only has 250 priority but priority to hug content is higher, it chooses to hug content. By default constraints are set to 1000, which is why it only works by setting priority lower for engine to "break" constraint, I prefer having the >= constraint as in Nikolai's answer – NSGangster Nov 06 '19 at 22:22
4

You would do that by removing the minimum height.

If you need the minimum height to something else below the label then you would use a container view that resized based on the label contents but used a minimum.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • Yes, the label height is connected to another view's top placed below the label. I understood your solution, but that makes my tablecellview more complicated, it already have a lot of constrains connecting each other. Will you please suggest me another solution like sizetofit ? – MELWIN Feb 19 '15 at 12:02
  • Size to fit reduces the height of the label, so it's more complicated because you have specified different requirements... – Wain Feb 19 '15 at 12:24
2

There is an easy solution for cases where the height of a label doesn't need to be constant: put your Label in a Stack View. Be sure to add leading and trailing constants to the Stack View. Here is a screenshot of how to do it in storyboard:

enter image description here

1

Auto layout only work with edges/sizes of controller, not with controllers content.so its not a good idea to use auto layout to display your label text on top of first line. according to me sizetofit is a best option to do so.

Bhavin Bhadani
  • 22,224
  • 10
  • 78
  • 108
1

I used @Daniel Golasko's solution and whenever the text inside the UILabel was longer than the UILabel could contain, the text would start moving down instead of staying aligned to top.

I changed this line to make sure the text is aligned properly

    [super drawTextInRect:CGRectMake(0, 0, ceilf(CGRectGetWidth(self.frame)),MIN(ceilf(labelStringSize.height), self.frame.size.height))];
ezchen
  • 41
  • 1
  • 3
1

I had a similiar issue where there were 3 labels. The middle label could have much longer text than the other two, so its height could grow much larger.

Like this:

enter image description here

I set the middle label's bottom space constraint to be >= the bottom label.

enter image description here

That solved my problem.

Pætur Magnussen
  • 901
  • 1
  • 11
  • 24
1

Here's an improvement on the Swift 3 solution by Daniel Galasko (here you can also set the maximum line number without an offset on the top):

import UIKit

@IBDesignable class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        if let stringText = text {
            let stringTextAsNSString = stringText as NSString
            let labelString = stringTextAsNSString.boundingRect(with: CGSize(width: frame.width, height: .greatestFiniteMagnitude),
                    options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
            super.drawText(in: CGRect(x: 0, y: 0, width: frame.width, height: ceil(labelString.size.height) > frame.height ? frame.height : ceil(labelString.size.height)))
        } else {
            super.drawText(in: rect)
        }
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        layer.borderWidth = 1
        layer.borderColor = UIColor.black.cgColor
    }
}
Onno Eberhard
  • 1,341
  • 1
  • 10
  • 18
1

Swift 4

You should subclass UILabel and override text display rendering.

class UITopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        guard let string = text else {
            super.drawText(in: rect)
            return
        }

        let size = (string as NSString).boundingRect(
            with: CGSize(width: rect.width, height: .greatestFiniteMagnitude),
            options: [.usesLineFragmentOrigin],
            attributes: [.font: font],
            context: nil).size

        var rect = rect
        rect.size.height = size.height.rounded()
        super.drawText(in: rect)
    }
}
dimpiax
  • 12,093
  • 5
  • 62
  • 45
0

You can try if button [button setContentVerticalAlignment:UIControlContentVerticalAlignmentTop];

Edit :

You can try with this if you want to use label only:

https://stackoverflow.com/a/11278660/1223897

Community
  • 1
  • 1
Yuvrajsinh
  • 4,536
  • 1
  • 18
  • 32
  • I didnt get the option [label setContentVerticalAlignmen...] for UILabel. It is for Button only right ? – MELWIN Feb 19 '15 at 13:17
0

For me, I didn't set the height constraint, the text always grows from the top of the label. The constraints for this label are top, left, right. By the way, my label has fixed line numbers, so no worries about the height.

Xiang LI
  • 345
  • 1
  • 9
0
  @IBInspectable var alignTop: Bool = false

  func setAlignTop() {
    let text = self.text!
    let lines = text.characters.split(separator: "\n").count
    if lines < self.numberOfLines {
      var newLines = ""
      for _ in 0..<(self.numberOfLines - lines) {
        newLines = newLines.appending("\n ")
      }
      self.text! = text.appending(newLines)
    }
  }

  override var text: String? {
    didSet {
      if alignTop {
        self.setAlignTop()
      }
    }
  }
hojin
  • 1,221
  • 14
  • 16
0

use this my class, you can change text alignment by contentMode.

supported case: .top, .bottom, .left, .right, .topLeft, .topRight, .bottomLeft, .bottomRight

Swift4

import Foundation
import UIKit

@IBDesignable
class UIAlignedLabel: UILabel {

    override func drawText(in rect: CGRect) {
        if let text = text as NSString? {
            func defaultRect(for maxSize: CGSize) -> CGRect {
                let size = text
                    .boundingRect(
                        with: maxSize,
                        options: NSStringDrawingOptions.usesLineFragmentOrigin,
                        attributes: [
                            NSAttributedStringKey.font: font
                        ],
                        context: nil
                    ).size
                let rect = CGRect(
                    origin: .zero,
                    size: CGSize(
                        width: min(frame.width, ceil(size.width)),
                        height: min(frame.height, ceil(size.height))
                    )
                )
                return rect

            }
            switch contentMode {
            case .top, .bottom, .left, .right, .topLeft, .topRight, .bottomLeft, .bottomRight:
                let maxSize = CGSize(width: frame.width, height: frame.height)
                var rect = defaultRect(for: maxSize)
                switch contentMode {
                    case .bottom, .bottomLeft, .bottomRight:
                        rect.origin.y = frame.height - rect.height
                    default: break
                }
                switch contentMode {
                    case .right, .topRight, .bottomRight:
                        rect.origin.x = frame.width - rect.width
                    default: break
                }
                super.drawText(in: rect)
            default:
                super.drawText(in: rect)
            }
        } else {
            super.drawText(in: rect)
        }
    }

}
EvGeniy Ilyin
  • 1,817
  • 1
  • 21
  • 38
0

In the Interface Builder, just make the height <= some value instead of =. This will enable to text to start at the top and expand the height as needed. For example, I have a label with a height proportional to the size of the main view. So my height constraint looks like this: Height Constraint