Vertically align text within a UILabel (Note : Using AutoLayout)

IosObjective CCocoa TouchAutolayoutUilabel

Ios Problem Overview


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

Ios Solutions


Solution 1 - Ios

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

Solution 2 - Ios

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.

Solution 3 - Ios

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

Solution 4 - Ios

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

Solution 5 - Ios

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

Solution 6 - Ios

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.

Solution 7 - Ios

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.

Solution 8 - Ios

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

Solution 9 - Ios

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.

Solution 10 - Ios

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

Solution 11 - Ios

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

Solution 12 - Ios

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

Solution 13 - Ios

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

Solution 14 - Ios

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.

Solution 15 - Ios

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

Solution 16 - Ios

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

Solution 17 - Ios

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMELWINView Question on Stackoverflow
Solution 1 - IosDaniel GalaskoView Answer on Stackoverflow
Solution 2 - IosNikola MilicevicView Answer on Stackoverflow
Solution 3 - IosarturdevView Answer on Stackoverflow
Solution 4 - IosOyvkvaView Answer on Stackoverflow
Solution 5 - IosFelipe FMMobileView Answer on Stackoverflow
Solution 6 - IosWainView Answer on Stackoverflow
Solution 7 - IosBhavin BhadaniView Answer on Stackoverflow
Solution 8 - IosezchenView Answer on Stackoverflow
Solution 9 - IosPætur MagnussenView Answer on Stackoverflow
Solution 10 - IosOnno EberhardView Answer on Stackoverflow
Solution 11 - IosAlexander PoleschukView Answer on Stackoverflow
Solution 12 - IosdimpiaxView Answer on Stackoverflow
Solution 13 - IosYuvrajsinhView Answer on Stackoverflow
Solution 14 - IosXiang LIView Answer on Stackoverflow
Solution 15 - IoshojinView Answer on Stackoverflow
Solution 16 - IosEvGeniy IlyinView Answer on Stackoverflow
Solution 17 - IosRob AdamsonView Answer on Stackoverflow