333

I'm looking to set the left inset/margin of a UILabel and can't find a method to do so. The label has a background set so just changing its origin won't do the trick. It would be ideal to inset the text by 10px or so on the left hand side.

Bhavin Ramani
  • 3,221
  • 5
  • 30
  • 41
Ljdawson
  • 12,091
  • 11
  • 45
  • 60
  • once you subclass, for the insets it is simply, https://stackoverflow.com/a/43197662/294884 – Fattie Jun 05 '18 at 02:20
  • Another approach may be embedding your label in a horizontal stackview and add uiview left / right of any width you wish. – ugur Jun 06 '18 at 14:11
  • We have finally, finally completely solved this problem. You must call super LAST in textRect: https://stackoverflow.com/a/58876988/294884 – Fattie Nov 15 '19 at 12:23

38 Answers38

470

I solved this by subclassing UILabel and overriding drawTextInRect: like this:

- (void)drawTextInRect:(CGRect)rect {
    UIEdgeInsets insets = {0, 5, 0, 5};
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}

Swift 3.1:

override func drawText(in rect: CGRect) {
    let insets = UIEdgeInsets.init(top: 0, left: 5, bottom: 0, right: 5)
    super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
}

Swift 4.2.1:

override func drawText(in rect: CGRect) {
    let insets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    super.drawText(in: rect.inset(by: insets))
}

As you might have gathered, this is an adaptation of tc.'s answer. It has two advantages over that one:

  1. there's no need to trigger it by sending a sizeToFit message
  2. it leaves the label frame alone - handy if your label has a background and you don't want that to shrink
Roman Podymov
  • 4,168
  • 4
  • 30
  • 57
Tommy Herbert
  • 20,407
  • 14
  • 52
  • 57
  • 2
    what is the point of "return" here? – Radu Simionescu Jan 20 '14 at 23:10
  • 15
    You might want to check out [this answer](http://stackoverflow.com/a/21267507/104790) which properly deals with sizeToFit and auto layout. – Nikolai Ruhe Jan 21 '14 at 19:48
  • I'm finding that this approach doesn't play nice with sizeWithFont:constrainedToSize:... frame resizing; is that anyone else's experience? – weienw Jan 23 '14 at 19:04
  • 2
    You will also need to subclass `editingRectForBounds:` if you want to have the inset while entering text. – fabb May 09 '14 at 09:51
  • It could be useful to add the insets as a property to the new class, so the user could adjust them as needed. e.g. @property (nonatomic) UIEdgeInsets edgeInsets; – Don Miguel Jun 23 '14 at 13:56
  • I had to override `-sizeThatFits:(CGSize)size` as well, since my subclass does some calculations for font size etc. Thanks, that's a brilliant solution! – Nir Golan Jul 17 '14 at 21:57
  • 21
    You should also override `intrinsicContentSize` so that it works with auto layout. I've added that to the sample code in [@Brody's](http://stackoverflow.com/a/17557490/1693173) answer. – progrmr Jul 28 '14 at 13:02
  • 13
    I don't understand how this answer could get so many upvotes?! This approach will most probably cause many issue regarding lineBreakingMode and placement of ellipsis. The computed needed size for the string isn't equal the size given to drawing it, or am I wrong? – Patrik Mar 02 '15 at 15:09
  • `drawTextInRect` is public so must be `override public func drawTextInRect` – Stepan Maksymov Sep 01 '16 at 12:06
  • I am getting runtime exception "can not cast..." . I want to do the same thing with a UILabel obtained from viewWithTag method of UITableViewCell. Anyone can help ? – Abdul Hannan Sep 14 '16 at 15:41
  • Its not working for me, here is my code: https://stackoverflow.com/q/45218242/2652541 – kurrodu Jul 20 '17 at 23:17
  • overriding drawTextInRect cannot work correctly for NSAttributedString – Harrison Xi Mar 28 '18 at 15:09
  • I think the most simple way is adding some spaces to that particular string:[UILabel setText:[NSString stringWithFormat:@" %@ ", text]]; XD – steven Nov 08 '18 at 10:50
  • This is good without constraints. When I apply auto layout constraints, this won't work..... because in the given width and height, it will apply the padding and text will be truncated. – Satyam Aug 31 '19 at 07:31
  • This is quite wrong. The full answer: https://stackoverflow.com/a/58876988/294884 – Fattie Nov 15 '19 at 12:24
  • Failed to set (topInset) user defined inspected property on (SLMobile.TextTopLeftLabel): [ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key topInset. Doesn't work. – John Pitts Aug 12 '21 at 16:33
153

For multiline text the left and the right margin can be set by using NSAttributedString.

NSMutableParagraphStyle *style =  [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
style.alignment = NSTextAlignmentJustified;
style.firstLineHeadIndent = 10.0f;
style.headIndent = 10.0f;
style.tailIndent = -10.0f;   

NSAttributedString *attrText = [[NSAttributedString alloc] initWithString:title attributes:@{ NSParagraphStyleAttributeName : style}];  

UILabel * label = [[UILabel alloc] initWithFrame:someFrame];
label.numberOfLines = 0;
label.attributedText = attrText;

Here is the above example adopted to Swift 5:

extension UILabel {
    func setMargins(margin: CGFloat = 10) {
        if let textString = self.text {
            var paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.firstLineHeadIndent = margin
            paragraphStyle.headIndent = margin
            paragraphStyle.tailIndent = -margin
            let attributedString = NSMutableAttributedString(string: textString)
            attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
            attributedText = attributedString
        }
    }
}
Zorayr
  • 23,770
  • 8
  • 136
  • 129
blyabtroi
  • 2,046
  • 1
  • 15
  • 22
96

The best approach to add padding to a UILabel is to subclass UILabel and add an edgeInsets property. You then set the desired insets and the label will be drawn accordingly.

OSLabel.h

#import <UIKit/UIKit.h>

@interface OSLabel : UILabel

@property (nonatomic, assign) UIEdgeInsets edgeInsets;

@end

OSLabel.m

#import "OSLabel.h"

@implementation OSLabel

- (id)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.edgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
    }
    return self;
}

- (void)drawTextInRect:(CGRect)rect {
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, self.edgeInsets)];
}

- (CGSize)intrinsicContentSize
{
    CGSize size = [super intrinsicContentSize];
    size.width  += self.edgeInsets.left + self.edgeInsets.right;
    size.height += self.edgeInsets.top + self.edgeInsets.bottom;
    return size;
}

@end
progrmr
  • 75,956
  • 16
  • 112
  • 147
Brody Robertson
  • 8,506
  • 2
  • 47
  • 42
  • 6
    Or use TTTAttributedLabel (https://github.com/mattt/TTTAttributedLabel) – Ajith Sep 24 '13 at 13:58
  • 5
    There's a problem with this solution - the last line of the label text will be cut off if the text is sufficiently long enough and if the insets are large enough. Just tried with the latest iOS 7. – BVB Jul 23 '14 at 18:06
  • 5
    You should also override `intrinsicContentSize` to increase the intrinsic size to include the insets so that auto layout will work properly. – progrmr Jul 28 '14 at 01:39
  • 1
    It truncates the text when all four edgeInsets are set. – Asif Bilal Jan 22 '15 at 16:32
  • 3
    It truncates my text if I set `numberOfLines = 0` :( – Ferran Maylinch Dec 05 '15 at 16:14
  • 2
    @AsifBilal you need to also override the `textRectForBounds:` method. – Kostub Deshmukh Oct 28 '16 at 07:02
  • This is not quite the full answer. Note the image example showing the problem here: https://stackoverflow.com/a/58876988/294884 – Fattie Nov 15 '19 at 12:25
76

Subclassing is a little cumbersome for such a simple case. An alternative is to simply add the UILabel with no background set to a UIView with the background set. Set the label's x to 10 and make the outer view's size 20 pixels wider than the label.

Peter DeWeese
  • 18,141
  • 8
  • 79
  • 101
  • 8
    *cough* the outer view would need to be _20_ points wider than the label. 10 on each side. – Tommy Feb 26 '16 at 17:25
  • 2
    While subclassing would create a reusable component, this approach did save me time. Thank you, Peter – SagarU Apr 20 '18 at 20:59
  • Keep in mind that the UILabel already is already subclassing a UIView so doing this would be a bit redundant, but it does achieve the goal. – Cyril Jun 23 '20 at 18:51
  • Oftentimes in XCode, we look for the complex answers. This the simplest and most effective and layering views can solve many more problems. In fact, back in the day, NS (NextStep) designed views for this very purpose. With the advent of constraints, many of us have forgotten just how simple (and fast) things can be by just using views. – PDG Sep 09 '20 at 18:09
50

With Swift 3, you can have the desired effect by creating a subclass of UILabel. In this subclass, you will have to add a UIEdgeInsets property with the required insets and override drawText(in:) method, intrinsicContentSize property (for Auto layout code) and/or sizeThatFits(_:) method (for Springs & Struts code).

import UIKit

class PaddingLabel: UILabel {

    let padding: UIEdgeInsets

    // Create a new PaddingLabel instance programamtically with the desired insets
    required init(padding: UIEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)) {
        self.padding = padding
        super.init(frame: CGRect.zero)
    }

    // Create a new PaddingLabel instance programamtically with default insets
    override init(frame: CGRect) {
        padding = UIEdgeInsets.zero // set desired insets value according to your needs
        super.init(frame: frame)
    }

    // Create a new PaddingLabel instance from Storyboard with default insets
    required init?(coder aDecoder: NSCoder) {
        padding = UIEdgeInsets.zero // set desired insets value according to your needs
        super.init(coder: aDecoder)
    }

    override func drawText(in rect: CGRect) {
        super.drawText(in: UIEdgeInsetsInsetRect(rect, padding))
    }

    // Override `intrinsicContentSize` property for Auto layout code
    override var intrinsicContentSize: CGSize {
        let superContentSize = super.intrinsicContentSize
        let width = superContentSize.width + padding.left + padding.right
        let height = superContentSize.height + padding.top + padding.bottom
        return CGSize(width: width, height: height)
    }

    // Override `sizeThatFits(_:)` method for Springs & Struts code
    override func sizeThatFits(_ size: CGSize) -> CGSize {
        let superSizeThatFits = super.sizeThatFits(size)
        let width = superSizeThatFits.width + padding.left + padding.right
        let heigth = superSizeThatFits.height + padding.top + padding.bottom
        return CGSize(width: width, height: heigth)
    }

}

The following example shows how to use PaddingLabel instances in a UIViewController:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var storyboardAutoLayoutLabel: PaddingLabel!
    let autoLayoutLabel = PaddingLabel(padding: UIEdgeInsets(top: 20, left: 40, bottom: 20, right: 40))
    let springsAndStructsLabel = PaddingLabel(frame: CGRect.zero)
    var textToDisplay = "Lorem ipsum dolor sit er elit lamet."

    override func viewDidLoad() {
        super.viewDidLoad()

        // Set autoLayoutLabel
        autoLayoutLabel.text = textToDisplay
        autoLayoutLabel.backgroundColor = .red
        autoLayoutLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(autoLayoutLabel)
        autoLayoutLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
        autoLayoutLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

        // Set springsAndStructsLabel
        springsAndStructsLabel.text = textToDisplay
        springsAndStructsLabel.backgroundColor = .green
        view.addSubview(springsAndStructsLabel)
        springsAndStructsLabel.frame.origin = CGPoint(x: 30, y: 90)
        springsAndStructsLabel.sizeToFit()

        // Set storyboardAutoLayoutLabel
        storyboardAutoLayoutLabel.text = textToDisplay
        storyboardAutoLayoutLabel.backgroundColor = .blue
    }

    // Link this IBAction to a UIButton or a UIBarButtonItem in Storyboard
    @IBAction func updateLabelText(_ sender: Any) {
        textToDisplay = textToDisplay == "Lorem ipsum dolor sit er elit lamet." ? "Lorem ipsum." : "Lorem ipsum dolor sit er elit lamet."

        // autoLayoutLabel
        autoLayoutLabel.text = textToDisplay

        // springsAndStructsLabel
        springsAndStructsLabel.text = textToDisplay
        springsAndStructsLabel.sizeToFit()

        // storyboardAutoLayoutLabel
        storyboardAutoLayoutLabel.text = textToDisplay
    }

}
Pavel Alexeev
  • 6,026
  • 4
  • 43
  • 51
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
  • The implementation is missing override for `textRect(forBounds:limitedToNumberOfLines:)` with call to super with bounds set to `UIEdgeInsetsInsetRect(bounds, padding)`, otherwise the text might get truncated - size calculation is not correct when the view size constrained (so `intrinsicContentSize` is not used). – Marián Černý Sep 18 '18 at 20:37
  • can you add properties so we can use it in storyboard and not programmatically? – user924 Apr 13 '19 at 21:05
48

Swift version of Recycled Steel's answer + intrinsizeContentSize().

It supports a more traditional style of setting insets for other view objects with insets while being able to set insets in Interface Builder, i.e. insets are set like so programmatically:

label.insets = UIEdgeInsetsMake(0, 0, 5, 0)

Please let me know if there are any bugs.

Swift 5

@IBInspectable var topInset: CGFloat = 0.0
@IBInspectable var leftInset: CGFloat = 0.0
@IBInspectable var bottomInset: CGFloat = 0.0
@IBInspectable var rightInset: CGFloat = 0.0

var insets: UIEdgeInsets {
    get {
        return UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
    }
    set {
        topInset = newValue.top
        leftInset = newValue.left
        bottomInset = newValue.bottom
        rightInset = newValue.right
    }
}

override func drawText(in rect: CGRect) {
    super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
}

override func sizeThatFits(_ size: CGSize) -> CGSize {
    var adjSize = super.sizeThatFits(size)
    adjSize.width += leftInset + rightInset
    adjSize.height += topInset + bottomInset
    
    return adjSize
}

override var intrinsicContentSize: CGSize {
    var contentSize = super.intrinsicContentSize
    contentSize.width += leftInset + rightInset
    contentSize.height += topInset + bottomInset
    
    return contentSize
}

Swift 4.2

@IBDesignable class InsetLabel: UILabel {
    @IBInspectable var topInset: CGFloat = 0.0
    @IBInspectable var leftInset: CGFloat = 0.0
    @IBInspectable var bottomInset: CGFloat = 0.0
    @IBInspectable var rightInset: CGFloat = 0.0
    
    var insets: UIEdgeInsets {
        get {
            return UIEdgeInsetsMake(topInset, leftInset, bottomInset, rightInset)
        }
        set {
            topInset = newValue.top
            leftInset = newValue.left
            bottomInset = newValue.bottom
            rightInset = newValue.right
        }
    }
    
    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: insets))
    }
    
    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var adjSize = super.sizeThatFits(size)
        adjSize.width += leftInset + rightInset
        adjSize.height += topInset + bottomInset
        
        return adjSize
    }
    
    override var intrinsicContentSize: CGSize {
        var contentSize = super.intrinsicContentSize
        contentSize.width += leftInset + rightInset
        contentSize.height += topInset + bottomInset
        
        return contentSize
    }
}

Swift 3

@IBDesignable class InsetLabel: UILabel {
    @IBInspectable var topInset: CGFloat = 0.0
    @IBInspectable var leftInset: CGFloat = 0.0
    @IBInspectable var bottomInset: CGFloat = 0.0
    @IBInspectable var rightInset: CGFloat = 0.0
    
    var insets: UIEdgeInsets {
        get {
            return UIEdgeInsetsMake(topInset, leftInset, bottomInset, rightInset)
        }
        set {
            topInset = newValue.top
            leftInset = newValue.left
            bottomInset = newValue.bottom
            rightInset = newValue.right
        }
    }
    
    override func drawText(in rect: CGRect) {
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }
    
    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var adjSize = super.sizeThatFits(size)
        adjSize.width += leftInset + rightInset
        adjSize.height += topInset + bottomInset
        
        return adjSize
    }
    
    override var intrinsicContentSize: CGSize {
        var contentSize = super.intrinsicContentSize
        contentSize.width += leftInset + rightInset
        contentSize.height += topInset + bottomInset
        
        return contentSize
    }
}

Swift 2.2

@IBDesignable class InsetLabel: UILabel {
    @IBInspectable var topInset: CGFloat = 0.0
    @IBInspectable var leftInset: CGFloat = 0.0
    @IBInspectable var bottomInset: CGFloat = 0.0
    @IBInspectable var rightInset: CGFloat = 0.0
    
    var insets: UIEdgeInsets {
        get {
            return UIEdgeInsetsMake(topInset, leftInset, bottomInset, rightInset)
        }
        set {
            topInset = newValue.top
            leftInset = newValue.left
            bottomInset = newValue.bottom
            rightInset = newValue.right
        }
    }
        
    override func drawTextInRect(rect: CGRect) {
        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
    }
        
    override func sizeThatFits(size: CGSize) -> CGSize {
        var adjSize = super.sizeThatFits(size)
        adjSize.width += leftInset + rightInset
        adjSize.height += topInset + bottomInset
        
        return adjSize
    }
    
    override func intrinsicContentSize() -> CGSize {
        var contentSize = super.intrinsicContentSize()
        contentSize.width += leftInset + rightInset
        contentSize.height += topInset + bottomInset
        
        return contentSize
    }
}
Community
  • 1
  • 1
funct7
  • 3,407
  • 2
  • 27
  • 33
42

Edit: This is really old. There are better solutions above.

I ended up just adding some spaces to the text:

self.titleLabel.text = [NSString stringWithFormat:@"    %@", self.titleLabel.text];

Ugly yet effective, and no subclassing required.

You can try "\t" as well. For a generic solution please refer to the accepted answer

Yariv Nissim
  • 13,273
  • 1
  • 38
  • 44
29

You can also solve this by initializing your UILabel with a custom frame.

    CGRect initialFrame = CGRectMake(0, 0, 100, 100);
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 10, 0, 0);
    CGRect paddedFrame = UIEdgeInsetsInsetRect(initialFrame, contentInsets);

    self.label = [[UILabel alloc] initWithFrame:paddedFrame];

Nod to CGRect Tricks.

neverbendeasy
  • 968
  • 10
  • 17
19

and an @IBDesignable that make it work with Interface Builder

Swift 4

//
//  PaddedLabel.swift
//  TrainCentric
//
//  Created by Arsonik
//  https://stackoverflow.com/a/33244365/337934
//

import UIKit

@IBDesignable
class PaddedLabel: UILabel {

    @IBInspectable var inset:CGSize = CGSize(width: 0, height: 0)

    var padding: UIEdgeInsets {
        var hasText:Bool = false
        if let t = self.text?.count, t > 0 {
            hasText = true
        }
        else if let t = attributedText?.length, t > 0 {
            hasText = true
        }

        return hasText ? UIEdgeInsets(top: inset.height, left: inset.width, bottom: inset.height, right: inset.width) : UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }

    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: padding))
    }

    override var intrinsicContentSize: CGSize {
        let superContentSize = super.intrinsicContentSize
        let p = padding
        let width = superContentSize.width + p.left + p.right
        let heigth = superContentSize.height + p.top + p.bottom
        return CGSize(width: width, height: heigth)
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        let superSizeThatFits = super.sizeThatFits(size)
        let p = padding
        let width = superSizeThatFits.width + p.left + p.right
        let heigth = superSizeThatFits.height + p.top + p.bottom
        return CGSize(width: width, height: heigth)
    }
}

Swift 2

@IBDesignable
class PaddedLabel: UILabel {

    @IBInspectable var inset:CGSize = CGSize(width: 0, height: 0)

    var padding: UIEdgeInsets {
        var hasText:Bool = false
        if let t = text?.length where t > 0 {
            hasText = true
        }
        else if let t = attributedText?.length where t > 0 {
            hasText = true
        }

        return hasText ? UIEdgeInsets(top: inset.height, left: inset.width, bottom: inset.height, right: inset.width) : UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }

    override func drawTextInRect(rect: CGRect) {
        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, padding))
    }

    override func intrinsicContentSize() -> CGSize {
        let superContentSize = super.intrinsicContentSize()
        let p = padding
        let width = superContentSize.width + p.left + p.right
        let heigth = superContentSize.height + p.top + p.bottom
        return CGSize(width: width, height: heigth)
    }

    override func sizeThatFits(size: CGSize) -> CGSize {
        let superSizeThatFits = super.sizeThatFits(size)
        let p = padding
        let width = superSizeThatFits.width + p.left + p.right
        let heigth = superSizeThatFits.height + p.top + p.bottom
        return CGSize(width: width, height: heigth)
    }
}
user924
  • 8,146
  • 7
  • 57
  • 139
Arsonik
  • 2,276
  • 1
  • 16
  • 24
18

For Xamarin users (using Unified API):

class UIMarginLabel : UILabel
{
    public UIMarginLabel()
    {
    }

    public UIMarginLabel( CGRect frame ) : base( frame )
    {
    }

    public UIEdgeInsets Insets { get; set; }

    public override void DrawText( CGRect rect )
    {
        base.DrawText( Insets.InsetRect( rect ) );
    }
}

And for those using the original MonoTouch API:

public class UIMarginLabel : UILabel
{
    public UIEdgeInsets Insets { get; set; }

    public UIMarginLabel() : base()
    {
        Insets = new UIEdgeInsets(0, 0, 0, 0);
    }
    public UIMarginLabel(RectangleF frame) : base(frame)
    {
        Insets = new UIEdgeInsets(0, 0, 0, 0);
    }

    public override void DrawText(RectangleF frame)
    {
        base.DrawText(new RectangleF(
            frame.X + Insets.Left,
            frame.Y + Insets.Top,
            frame.Width - Insets.Left - Insets.Right,
            frame.Height - Insets.Top - Insets.Bottom));
    }
}
Millie Smith
  • 4,536
  • 2
  • 24
  • 60
  • In the unified API example, RectangleF is used in the ctor; it should be CGRect in order to work – vividos May 25 '18 at 05:42
14

If you don't want to use an extra parent view to set the background, you can subclass UILabel and override textRectForBounds:limitedToNumberOfLines:. I'd add a textEdgeInsets property or similar and then do

- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
{
  return [super textRectForBounds:UIEdgeInsetsInsetRect(bounds,textEdgeInsets) limitedToNumberOfLines:numberOfLines];
}

For robustness, you might also want to call [self setNeedsDisplay] in setTextEdgeInsets:, but I usually don't bother.

tc.
  • 33,468
  • 5
  • 78
  • 96
  • Note that, from the documentation, *"For this method to be called, there must be a prior call to the sizeToFit or sizeThatFits: method."* – mvds Jan 08 '11 at 21:46
  • 2
    @mvds: That's fine: `textRectForBounds:limitedToNumberOfLines:` is being called, so it's up to whoever called it to ensure that there's been a prior call to `-sizeToFit`/`-sizeThatFits:`. – tc. Jan 20 '11 at 16:00
  • This doesn't appear to be working in Xcode 9.3/Swift 4.1/iOS 11.3. `textRectForBounds()` is being called and my edge insets are present, but the label doesn't have any of this padding. I am seeing odd values for the width and the height. This is a description of `bounds` from `textRectForBounds()`: `Printing description of bounds: ▿ (0.0, 0.0, 3.40282346638529e+38, 3.40282346638529e+38)`. The label is being created in `viewDidLoad()` in a view controller. – Evan R Apr 10 '18 at 19:40
  • 1
    Adding this code to Brody Robertson's answer finally worked for me. Without overriding textRectForBounds:limitedToNumberOfLines: the last line of my string (a multiline attributed string converted from html) would be cut off. I am using Xcode 10.0 and iOS 12. – Alyoshak Dec 04 '18 at 17:31
13

To expand on the answer provided by Brody Robertson you can add the IB Designable bits. This means you can adjust the label from within Storyboard.

enter image description here

In your subclassed UILabel do

#import <UIKit/UIKit.h>

IB_DESIGNABLE

@interface insetLabel : UILabel

@property (nonatomic, assign) IBInspectable CGFloat leftEdge;
@property (nonatomic, assign) IBInspectable CGFloat rightEdge;
@property (nonatomic, assign) IBInspectable CGFloat topEdge;
@property (nonatomic, assign) IBInspectable CGFloat bottomEdge;

@property (nonatomic, assign) UIEdgeInsets edgeInsets;

@end

Then do;

#import "insetLabel.h"

@implementation insetLabel

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        self.edgeInsets = UIEdgeInsetsMake(self.topEdge, self.leftEdge, self.bottomEdge, self.rightEdge);
    }
    return self;
}

- (void)drawTextInRect:(CGRect)rect
{
    self.edgeInsets = UIEdgeInsetsMake(self.topEdge, self.leftEdge, self.bottomEdge, self.rightEdge);

    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, self.edgeInsets)];
}

- (CGSize)intrinsicContentSize
{
    CGSize size = [super intrinsicContentSize];
    size.width  += self.edgeInsets.left + self.edgeInsets.right;
    size.height += self.edgeInsets.top + self.edgeInsets.bottom;
    return size;
}

@end

EDIT

You should probably add a setter method for edgeInsets.

Recycled Steel
  • 2,272
  • 3
  • 30
  • 35
  • Please add this to your response so it actually works: - (void)awakeFromNib { self.edgeInsets = UIEdgeInsetsMake(self.topEdge, self.leftEdge, self.bottomEdge, self.rightEdge); } – Pauls Oct 01 '15 at 10:41
  • Autolayout should be fine, does it show up in Storyboard? Also I have just seen Pauls answer above, have you added the awakeFromNib? – Recycled Steel Apr 05 '16 at 09:41
  • Also, have you changed your Label class to the custom class? Third icon along in the top right. – Recycled Steel Apr 05 '16 at 09:44
  • @RecycledSteel I am having some problem, the above method works, but Its not resizing itself! I have set constraints on horizontally and vertically center. What should I do so that It gets update in size with the change in the Label Text – Jasmeet May 17 '16 at 08:50
11

Maybe later for the party, but the following just works. Just subclass UILabel.

#import "UITagLabel.h"

#define padding UIEdgeInsetsMake(5, 10, 5, 10)

@implementation UITagLabel

- (void)drawTextInRect:(CGRect)rect {
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, padding)];
}

- (CGSize) intrinsicContentSize {
    CGSize superContentSize = [super intrinsicContentSize];
    CGFloat width = superContentSize.width + padding.left + padding.right;
    CGFloat height = superContentSize.height + padding.top + padding.bottom;
    return CGSizeMake(width, height);
}

- (CGSize) sizeThatFits:(CGSize)size {
    CGSize superSizeThatFits = [super sizeThatFits:size];
    CGFloat width = superSizeThatFits.width + padding.left + padding.right;
    CGFloat height = superSizeThatFits.height + padding.top + padding.bottom;
    return CGSizeMake(width, height);
}

@end
valvoline
  • 7,737
  • 3
  • 47
  • 52
9

Here is a swift solution. Just add this custom class on the bottom of your file (or create a new file for it) and use MyLabel instead of UILabel when creating your label.

class MyLabel: UILabel{
    override func drawTextInRect(rect: CGRect) {
        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)))
    }
}
rsc
  • 10,348
  • 5
  • 39
  • 36
8

I didn't find the suggestion to use UIButton in the answers above. So I will try to prove that this is a good choice.

button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)

In my situation using UIButton was the best solution because:

  • I had a simple single-line text
  • I didn't want to use UIView as a container for UILabel (i.e. I wanted to simplify math calculations for Autolayout in my cell)
  • I didn't want to use NSParagraphStyle (because tailIndent works incorrect with Autolayout – width of UILabel is smaller than expected)
  • I didn't want to use UITextView (because of possible side effects)
  • I didn't want to subclass UILabel (less code fewer bugs)

That's why using the contentEdgeInsets from UIButton in my situation became the easiest way to add text margins.

Hope this will help someone.

sig
  • 6,024
  • 2
  • 26
  • 31
7

If you're using autolayout in iOS 6+, you can do this by adjusting the intrinsicContentSize in a subclass of UILabel.

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.textAlignment = NSTextAlignmentRight;
    }
    return self;
}

- (CGSize)intrinsicContentSize 
{
    CGSize size = [super intrinsicContentSize];
    return CGSizeMake(size.width + 10.0, size.height);
}
Ian Terrell
  • 10,667
  • 11
  • 45
  • 66
7

blyabtroi's asnwer converted in Swift (No Subclassing required)

let style: NSMutableParagraphStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle
style.alignment = .Justified
style.firstLineHeadIndent = 10.0
style.headIndent = 10.0
style.tailIndent = -10.0
let attrText: NSAttributedString = NSAttributedString(string: title, attributes: [NSParagraphStyleAttributeName:style])
let label: UILabel = UILabel(frame: someFrame)
label.numberOfLines = 0
label.attributedText = attrText
Abdul Hannan
  • 243
  • 2
  • 9
7

Swift 4 version of blyabtroi solution

let leadingMargin: CGFloat = 10
let trailingMargin: CGFloat = 10

let style = NSMutableParagraphStyle()
style.alignment = .justified
style.firstLineHeadIndent = leadingMargin
style.headIndent = leadingMargin
style.tailIndent = trailingMargin

label.attributedText = NSAttributedString(string: "Label with margins", 
                                          attributes: [NSAttributedStringKey.paragraphStyle: style])
Adri Silva
  • 111
  • 2
  • 2
6

Instead of UILabel perhaps use https://github.com/mattt/TTTAttributedLabel

BITAttributedLabel *label = [BITAttributedLabel new];
label.font = font;
label.text = @"hello";
label.textInsets = UIEdgeInsetsMake(10, 10, 10, 10);
[label sizeToFit];
Peter DeWeese
  • 18,141
  • 8
  • 79
  • 101
neoneye
  • 50,398
  • 25
  • 166
  • 151
5

In Swift it solves like this.

class Label: UILabel {
    override func drawTextInRect(rect: CGRect) {
        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)))
    }
}
Andrey Gagan
  • 1,338
  • 13
  • 13
5

This works correctly with multi-line labels:

class PaddedLabel: UILabel {
    var verticalPadding: CGFloat = 0
    var horizontalPadding: CGFloat = 0

    override func drawText(in rect: CGRect) {
        let insets = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

    override var intrinsicContentSize: CGSize {
        get {
            let textWidth = super.intrinsicContentSize.width - horizontalPadding * 2
            let textHeight = sizeThatFits(CGSize(width: textWidth, height: .greatestFiniteMagnitude)).height
            let width = textWidth + horizontalPadding * 2
            let height = textHeight + verticalPadding * 2
            return CGSize(width: frame.width, height: height)
        }
    }
}
fhucho
  • 34,062
  • 40
  • 136
  • 186
4

A lot of the answers are missing the override of sizeThatFits. With this subclass you can just create the label, set the padding, and then say label.SizeToFit() and voila.

import UIKit

class UILabelEx : UILabel
{
    var padding : UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

    override func drawTextInRect(rect: CGRect) {

        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, padding))
    }

    override func sizeThatFits(size: CGSize) -> CGSize
    {

        var adjSize = super.sizeThatFits(size)
        adjSize.width += padding.left + padding.right
        adjSize.height += padding.top + padding.bottom

        return adjSize
    }
}
Quincy
  • 1,710
  • 14
  • 20
4

Swift 3 & AutoLayout compatible version:

class InsetLabel: UILabel {

    var insets = UIEdgeInsets()

    convenience init(insets: UIEdgeInsets) {
        self.init(frame: CGRect.zero)
        self.insets = insets
    }

    convenience init(dx: CGFloat, dy: CGFloat) {
        let insets = UIEdgeInsets(top: dy, left: dx, bottom: dy, right: dx)
        self.init(insets: insets)
    }

    override func drawText(in rect: CGRect) {
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

    override var intrinsicContentSize: CGSize  {
        var size = super.intrinsicContentSize
        size.width += insets.left + insets.right
        size.height += insets.top + insets.bottom
        return size
    }
}
mokagio
  • 16,391
  • 3
  • 51
  • 58
Avt
  • 16,927
  • 4
  • 52
  • 72
3

This is the easiest solution I found for this:

Swift 4

class CustomLabel: UILabel{
    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: UIEdgeInsets.init(top: 10, left: 10, bottom: 10, right: 10)))
    }
}

Make sure you set your labels to the CustomLabel class in code as well as in storyboard.

Kaiusee
  • 1,253
  • 1
  • 14
  • 28
2

Xcode 6.1.1 Swift solution using a extension.

The file name could be something like "UILabel+AddInsetMargin.swift":

import UIKit

extension UILabel
{
    public override func drawRect(rect: CGRect)
    {
        self.drawTextInRect(UIEdgeInsetsInsetRect(rect, UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)))
    }
}
Andrew
  • 3,733
  • 1
  • 35
  • 36
MB_iOSDeveloper
  • 4,178
  • 4
  • 24
  • 36
  • Using extension to override an existing method in the main part of a class is not a good practice, except that `UILabel` does NOT and will NEVER write the method. – DawnSong Oct 12 '16 at 10:04
  • Whoa, hang on... you want to change the behaviour of every single UILabel? Potentially even for objects in frameworks you import, or other code you use? Someone else comes to use your code and can't figure out why a normal UILabel has an inset? Please, never ever do this. Only 'add' functionality with extensions, never 'change' functionality. – Jordan Smith Nov 11 '16 at 02:57
2

without subclassing and all that jazz.. i did this dynamically:

[cell.textLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[cell.textLabel constraintTrailingEqualTo:cell.contentView constant:-100];

the constraint part is just a simple code sugar wrapper (we have the same methods for adding a padding from top/bottom/left/right).. i'll open source the whole wrapper if i get enough love here:

- (id)constraintTrailingEqualTo:(UIView *)toView constant:(CGFloat)constant
{
    NSLayoutConstraint *cn = [NSLayoutConstraint constraintWithItem:self
                                                          attribute:NSLayoutAttributeTrailing
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:toView
                                                          attribute:NSLayoutAttributeTrailing
                                                         multiplier:1 constant:constant];

    [toView addConstraint:cn];
    return self;
}

(note i did this in the context of

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath;

you may have to call [self setNeedsLayout]; depending on your context.

abbood
  • 23,101
  • 16
  • 132
  • 246
2
#import "E_LabelWithPadding.h"
#define padding UIEdgeInsetsMake(2, 0, 2, 0)
#define padding1 UIEdgeInsetsMake(0, 0, 0, 0)
@implementation E_LabelWithPadding
- (void)drawTextInRect:(CGRect)rect {
if (![self.text isEqualToString:@""]) {
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, padding)];
}else {
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, padding1)];
}

}

- (CGSize) intrinsicContentSize {
if (![self.text isEqualToString:@""]) {
    CGSize superContentSize = [super intrinsicContentSize];
    CGFloat width = superContentSize.width + padding.left + padding.right;
    CGFloat height = superContentSize.height + padding.top + padding.bottom;
    return CGSizeMake(width, height);
}else {
    CGSize superContentSize = [super intrinsicContentSize];
    CGFloat width = superContentSize.width + padding1.left + padding1.right;
    CGFloat height = superContentSize.height + padding1.top + padding1.bottom;
    return CGSizeMake(width, height);
}

}

- (CGSize) sizeThatFits:(CGSize)size {
if (![self.text isEqualToString:@""]) {
    CGSize superSizeThatFits = [super sizeThatFits:size];
    CGFloat width = superSizeThatFits.width + padding.left + padding.right;
    CGFloat height = superSizeThatFits.height + padding.top + padding.bottom;
    return CGSizeMake(width, height);
}else {
    CGSize superSizeThatFits = [super sizeThatFits:size];
    CGFloat width = superSizeThatFits.width + padding1.left + padding1.right;
    CGFloat height = superSizeThatFits.height + padding1.top + padding1.bottom;
    return CGSizeMake(width, height);
}

}

@end
Hemanshu Liya
  • 611
  • 6
  • 10
1

If label is created programmatically, padding can be calculated using the sizeThatFits method. If using more than one line, the text will be line broken at the max width value.

let text = UILabel()
let padding = 10
text.layer.cornerRadius = 5
text.layer.masksToBounds = true
text.text = "Hello"
text.font = UIFont(name: text.font.fontName, size: 18)
text.textAlignment = NSTextAlignment.center
text.numberOfLines = 1

let maxSize = CGSize(width: 100, height: 100)
var size = text.sizeThatFits(maxSize)
size.width = size.width + padding * 2
size.height = size.height + padding * 2
text.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
Paintoshi
  • 816
  • 10
  • 12
1

I had solved it using xcode builder (but surely this could be achieved with swift using constraints):

Just create a UIView of the relevant size And add a child of type UILabel to that view

and there you go, you have padding :)

xcode builder solution

Re'em
  • 1,869
  • 1
  • 22
  • 28
0

Maybe you could give this code a try

CGRect frame = btn.titleLabel.frame;
int indent = 20;
int inset = 20;
[btn.titleLabel setFrame:CGRectMake(frame.origin.x+inset,frame.origin.y,frame.size.width+indent,frame.size.height)];
christian mini
  • 1,662
  • 20
  • 39
Michael
  • 25
  • 1
  • 1
    Use frame instead of *frame, use inset, not indent (typo). But the effect is nearly the same like moving the label 20 points to the right. That means, if I give a background color, the color is moved as well. So the solution is (at least for me) not useful. – Jojo.Lechelt Mar 15 '13 at 10:49
  • Note the question was about UILabels, UILabels do not contain a `titleLabel`, this fix is for UIButtons, just my two cents. – Dan Clarke Apr 23 '14 at 20:01
0

To get rid of vertical padding for a single line label I did:

// I have a category method setFrameHeight; you'll likely need to modify the frame.
[label setFrameHeight:font.pointSize];

OR, without the category, use:

CGRect frame = label.frame;
frame.size.height = font.pointSize;
label.frame = frame;
Chris Prince
  • 7,288
  • 2
  • 48
  • 66
0

I think UILabel class have no method for setting margin. Why you not set the position of Label at required place?

See below code:

UILabel *label = [[UILabel alloc] init];
label.text = @"This is label";
label.frame = CGRectMake(0,0,100,100);

if from interface builder then just position Label by following:

yourLabel.frame = CGRectMake(0,0,100,100);
Adam
  • 26,549
  • 8
  • 62
  • 79
Hiren Gujarati
  • 1,039
  • 14
  • 32
0

Just add spaces to the left if it's a single line, more than 1 line will have 0 padding again.

[self.myLabel setText:[NSString stringWithFormat:@"   %@", self.myShortString]];
emotality
  • 12,795
  • 4
  • 39
  • 60
0

You need to calculate UILabel size when you put insets. It can have different number of lines because of text alignment, line break mode.

override func drawText(in rect: CGRect) {

    let size = self.sizeThatFits(UIEdgeInsetsInsetRect(rect, insets).size);
    super.drawText(in: CGRect.init(origin: CGPoint.init(x: insets.left, y: insets.top), size: size));
}

override var intrinsicContentSize: CGSize {

    var size = super.intrinsicContentSize;

    if text == nil || text?.count == 0 {
        return size;
    }

    size = self.sizeThatFits(UIEdgeInsetsInsetRect(CGRect.init(origin: CGPoint.zero, size: size), insets).size);
    size.width  += self.insets.left + self.insets.right;
    size.height += self.insets.top + self.insets.bottom;

    return size;
}

try iEun/InsetLabel

Claire Kim
  • 31
  • 5
-1

This is the easiest way I found. It works like a charm for me.

UIView *titleSection = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 100)];
[titleSection addSubview:titleSection];

UILabel *label = [[UILabel alloc] initWithFrame:CGRectInset(titleSection.frame, PADDING, 0)];
[titleSection addSubview:label];
debiasej
  • 980
  • 1
  • 14
  • 26
-2

A lot of these answers are complicated. In some cases, that's necessary. However, if you're reading this, your label has no left/right margin, and you just want a little padding, here's the whole solution:

Step 1: Add spaces at the end (literally, hit the spacebar a few times)

Step 2: Set the text alignment of the label to centered

Done

Dave G
  • 12,042
  • 7
  • 57
  • 83
  • This is a terrible way to do it since if you change the text length, or font size, or font style, you'll be constantly updating the amount of spaces. Also, this means you can never Localise your app (different languages) since the text will be different. – kakubei Nov 28 '18 at 14:31
-4

Set the label's textAlignment property to NSTextAlignmentRight and augment its width.

ma11hew28
  • 121,420
  • 116
  • 450
  • 651
-5

Don't code, Xcode !

Instead of using UILabel for this specific matter, I suggest you to take a look at UIButton. It gives, out of the box, the ability to set Content Insets (top, left, bottom, right) in the Size inspector. Set the desired margins, after that disable the button right in Xcode and done.

user1592811
  • 903
  • 10
  • 5
  • You're being downvoted because this question is about `UILabel` and you replied about `UIButton`. – Orlando Oct 19 '17 at 17:38
  • @Orlando I get it, no worries. However, for those looking for a solution that really works and with no overhead, they can try this and move on. – user1592811 Feb 01 '19 at 21:44