263

I have a UILabel where I want to add space in the top and in the bottom. With the minimum height in constraints, I've modified it to:

Enter image description here

To do this I've used:

override func drawTextInRect(rect: CGRect) {
    var insets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10.0)
    super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
}

But I've to find a different method because if I write more than two lines, the problem is the same:

Enter image description here

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Annachiara
  • 2,657
  • 2
  • 13
  • 10
  • We finally finally figured out exactly how to do this properly, in all dynamic cases, as a perfect drop-in replacement for UILabel with no need to re-layout or any other issues. PHEW. https://stackoverflow.com/a/58876988/294884 – Fattie Nov 15 '19 at 12:29

37 Answers37

305

I have tried with it on Swift 4.2, hopefully it work for you!

@IBDesignable class PaddingLabel: UILabel {

    @IBInspectable var topInset: CGFloat = 5.0
    @IBInspectable var bottomInset: CGFloat = 5.0
    @IBInspectable var leftInset: CGFloat = 7.0
    @IBInspectable var rightInset: CGFloat = 7.0

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

    override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(width: size.width + leftInset + rightInset,
                      height: size.height + topInset + bottomInset)
    }   

    override var bounds: CGRect {
        didSet {
            // ensures this works within stack views if multi-line
            preferredMaxLayoutWidth = bounds.width - (leftInset + rightInset)
        }
    } 
}

Or you can use CocoaPods here https://github.com/levantAJ/PaddingLabel

pod 'PaddingLabel', '1.2'

enter image description here

Tai Le
  • 8,530
  • 5
  • 41
  • 34
  • 7
    The uilabel width is not changing, causing the text become "..." – neobie May 12 '18 at 06:09
  • Please how do I implement it – nivla360 Aug 24 '18 at 11:10
  • 1
    @Tai Le , thanks for sharing, I have used it in tableview, I don't know why its triming the text , eg. student becomes studen, – vishwa.deepak Aug 30 '18 at 06:04
  • Note that if you add to the `super.intrinsicContentSize` and it is already at its max value because you didn't set `preferredMaxLayoutWidth`, you will get this extremely cryptic error: "This NSLayoutConstraint is being configured with a constant that exceeds internal limits. A smaller value will be substituted, but this problem should be fixed. Break on BOOL _NSLayoutConstraintNumberExceedsLimit(void) to debug. This will be logged only once. This may break in the future." – Tim Nov 10 '18 at 16:01
  • 1
    @Tim maybe you've meant to use `min` – ielyamani May 23 '19 at 19:25
  • @ielyamani you are correct, I did mean that - will delete comment and post a new one with `min` – Tim May 24 '19 at 20:16
  • Changing the return statement to `return CGSize(width: min(size.width + leftInset + rightInset, 10000), height: size.height + topInset + bottomInset)` solved the issue – Tim May 24 '19 at 20:16
  • 7
    A word of warning here. I've been using this solution in a UILabel subclass. When using these labels in multiline mode, in a vertical UIStackView, there's an issue. Sometimes the label seems to wrap the text without correctly sizing the label - so a word or 2 ends up missing from the end of the string. I don't have a solution right now. I'll write it up here if I make one. I spent hours poking at this issue, before proving it was here. – Darren Black Oct 04 '19 at 15:17
  • I can confirm that this solution does not work in a vertical UIStackView with multiple lines. – user246392 Oct 30 '19 at 14:43
  • 2
    To make it work in those situations, you need to override "setBounds" and set self.preferredMaxLayoutWidth to the bounds' width, minus your left and right insets – Arnaud Barisain-Monrose Nov 19 '19 at 12:10
  • Unfortunately this answer is quite wrong. It fails in many cases. The first image HERE: https://stackoverflow.com/a/58876988/294884 is exactly an example of how it fails. – Fattie May 05 '20 at 13:44
  • This is the only solution that works with multi-line autolayout as well as with single-line out of many I tried. Thanks! – mkll May 28 '20 at 20:59
  • @mkll - it absolutely does not work at all. (It works "sometimes" if by chance the line length is a certain length. You can clearly and obviously see an example of it not working, with an image example, right here: https://stackoverflow.com/a/58876988/294884 – Fattie Feb 26 '21 at 13:33
  • 1
    hi @DarrenBlack just noticed your comment. as you say this doesn't work at all. as you say it takes hours and hours to figure out what is happening - it's fully explained here: https://stackoverflow.com/a/58876988/294884 – Fattie Feb 26 '21 at 13:42
  • I did a version that also allows specifying a border (color, width, corner radius) based on this answer and another answer here (Fattie's answer) https://stackoverflow.com/a/58876988/826946. My question and answer are over here: https://stackoverflow.com/a/67317976/826946 – Andy Weinstein Apr 29 '21 at 13:06
  • Unfortunately it doesn't work, at all, in Swift anything, @LanceSamaria :) – Fattie Sep 10 '21 at 21:28
  • it makes no difference at all if you override var bounds, @ReimondHill . **The answer is simply completely wrong.** A number of people have given actual example images of it being broken. It's just, simply, completely wrong. It only "works" in the simplest of situations where there's one text alone with nothing below it and it happens to "work" because the text length is, by luck, correct. – Fattie Sep 11 '21 at 17:59
147

If you want to stick with UILabel, without subclassing it, Mundi has given you a clear solution.

If alternatively, you would be willing to avoid wrapping the UILabel with a UIView, you could use UITextView to enable the use of UIEdgeInsets (padding) or subclass UILabel to support UIEdgeInsets.

Using a UITextView would only need to provide the insets (Objective-C):

textView.textContainerInset = UIEdgeInsetsMake(10, 0, 10, 0);

Alternative, if you subclass UILabel, an example to this approach would be overriding the drawTextInRect method
(Objective-C)

- (void)drawTextInRect:(CGRect)uiLabelRect {
    UIEdgeInsets myLabelInsets = {10, 0, 10, 0};
    [super drawTextInRect:UIEdgeInsetsInsetRect(uiLabelRect, myLabelInsets)];
}

You could additionally provide your new subclassed UILabel with insets variables for TOP, LEFT, BOTTOM and RIGHT.

An example code could be:

In .h (Objective-C)

float topInset, leftInset,bottomInset, rightInset;

In .m (Objective-C)

- (void)drawTextInRect:(CGRect)uiLabelRect {
    [super drawTextInRect:UIEdgeInsetsInsetRect(uiLabelRect, UIEdgeInsetsMake(topInset,leftInset,bottomInset,rightInset))];
}

From what I have seen, it seems you have to override the intrinsicContentSize of the UILabel when subclassing it.

So you should override intrinsicContentSize like:

- (CGSize) intrinsicContentSize {
    CGSize intrinsicSuperViewContentSize = [super intrinsicContentSize] ;
    intrinsicSuperViewContentSize.height += topInset + bottomInset ;
    intrinsicSuperViewContentSize.width += leftInset + rightInset ;
    return intrinsicSuperViewContentSize ;
}

And add the following method to edit your insets, instead of editing them individually:

- (void) setContentEdgeInsets:(UIEdgeInsets)edgeInsets {
    topInset = edgeInsets.top;
    leftInset = edgeInsets.left;
    rightInset = edgeInsets.right;
    bottomInset = edgeInsets.bottom;
    [self invalidateIntrinsicContentSize] ;
}

It will update the size of your UILabel to match the edge insets and cover the multiline necessity you referred to.

After searching a bit I have found this Gist with an IPInsetLabel. If none of those solutions work you could try it out.

There was a similar question (duplicate) about this matter.
For a full list of available solutions, see this answer: UILabel text margin

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
nunofmendes
  • 3,731
  • 2
  • 32
  • 42
  • Sorry but I've alreay used: ` override func drawTextInRect(rect: CGRect) { var insets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10.0) super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets)) } ` it doesn't work because the result is the same, don't work dynamically.. – Annachiara Dec 13 '14 at 17:50
  • Did you tried with a UITextView instead of a UILabel? Or do you really need to use an UILabel? – nunofmendes Dec 13 '14 at 21:34
  • @Annachiara check the edit I've made. See if it works. – nunofmendes Dec 14 '14 at 11:54
  • Ok. Did it work the textview? Sorry for not writing in Swift but I am still in Obj-C mode. My goal with that code was to help you reach some conclusion. Hope it did. – nunofmendes Dec 14 '14 at 16:53
  • 1
    Using TextView and some storyboard settings and self.textview.textContainerInset = UIEdgeInsetsMake(0, 10, 10, 10); It finally works ! Thanks ! – Annachiara Dec 14 '14 at 16:58
  • The result was returned in `intrinsicContentSize` method will be wrong when `leftInset` and `rightInset` increase, b/c the width of label will be narrowed but the height not increase, so a part of text will be disappeared. To resolve this problem you need to re-calculate the height of text and assign it to `intrinsicSuperViewContentSize.height`. I also posted an answer in this thread. – Quang Tran Aug 18 '16 at 08:10
  • I think [this answer is better](http://stackoverflow.com/a/21267507/2604204) because works fine with `sizeToFit`. – Luiz Henrique Guimaraes Dec 26 '16 at 16:37
  • overriding drawTextInRect cannot work correctly for NSAttributedString – Harrison Xi Mar 28 '18 at 15:10
  • Note that if you add to the `[super intrinsicContentSize]` and it is already at its max value because you didn't set `preferredMaxLayoutWidth`, you will get this extremely cryptic error: "This NSLayoutConstraint is being configured with a constant that exceeds internal limits. A smaller value will be substituted, but this problem should be fixed. Break on BOOL _NSLayoutConstraintNumberExceedsLimit(void) to debug. This will be logged only once. This may break in the future." – Tim Nov 10 '18 at 16:01
  • `UITextView` FTW! Why customize when you can substitute for a native component? – Andrew Kirna May 23 '19 at 04:56
  • This fails when the text *happens to be certain lengths*. I included an image example in the answer: https://stackoverflow.com/a/58876988/294884 it's taken some 10 years for everyone to figure out exactly how to do this, geesh! – Fattie Nov 15 '19 at 12:30
  • Guys, UITextView is useless - it does not size to fit easily. UILabel has many, many advantages. – Fattie Jan 19 '20 at 18:04
  • It's unfortunate for people googling here that this absolutely wrong, 10 yr old answer, is ticked as correct! – Fattie Aug 11 '22 at 14:33
93

Swift 3

import UIKit

class PaddingLabel: UILabel {

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

   override func drawText(in rect: CGRect) {
      let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
      super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
   }

   override var intrinsicContentSize: CGSize {
      get {
         var contentSize = super.intrinsicContentSize
         contentSize.height += topInset + bottomInset
         contentSize.width += leftInset + rightInset
         return contentSize
      }
   }
}
Gabriel.Massana
  • 8,165
  • 6
  • 62
  • 81
zombie
  • 5,069
  • 3
  • 25
  • 54
  • just a little comment: set this class to label in identity inspector(custom class) and use new attribute in attribute inspector named label padding. also below 5 padding is effectless – iman kazemayni Jan 23 '18 at 10:48
  • 4
    This doesn't always work correctly with multiline labels, because when the label calculates its height, it assumes zero padding. – fhucho Feb 22 '18 at 16:37
91

You can do it properly from IB :

  1. change the text to attributed

attributed text

  1. go to dropdown list with "..."

enter image description here

  1. you will see some padding properties for the lines, paragraphs and text change indent first line or anything you want

enter image description here

  1. check the result

enter image description here

GPY
  • 3,832
  • 1
  • 16
  • 11
  • 1
    In my storyboard I can see the text change but when I run the app. The text doesn´t show the change... T_T.. my label is inside of an custom cell, are there any problem? – A. Trejo Feb 17 '16 at 20:11
  • 1
    @A.Trejo May be your custom cell set the label property at run time. – GPY Feb 19 '16 at 09:05
  • In SB right margin can't has negative value. Do it in code. `style.tailIndent = -30.0`. Still can't do top and bottom indent. – Nike Kov Nov 25 '16 at 05:41
  • 1
    Changes can appear on storyboard but when you run the app there are no changes. – Rhenz May 30 '17 at 02:41
  • 5
    This is not applicable when you set text programatically. – Neeraj Joshi Jul 16 '18 at 11:43
  • 5
    This is not the answer. You have only control over the first line indent, bot not the padding in all directions. – rommex Mar 05 '19 at 08:24
  • I used the combination of "height multiple" and "spacing" properties of the attributed text in the IB to achieve desired padding on top and bottom respectively. – nishkaush Nov 08 '19 at 01:53
72

Just use a UIButton, its already built in. Turn off all the extra button features and you have a label that you can set edge instets on.

let button = UIButton()
button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
button.setTitle("title", for: .normal)
button.tintColor = .white // this will be the textColor
button.isUserInteractionEnabled = false
mxcl
  • 26,392
  • 12
  • 99
  • 98
Steve M
  • 1,232
  • 9
  • 4
65

Just use a UIView as a superview and define a fixed margin to the label with auto layout.

Mundi
  • 79,884
  • 17
  • 117
  • 140
  • 2
    `drawTextInRect ` works only for 1 line, `intrinsicContentSize` does not work with horizontal padding. Wrap UILabel inside UIView is the good way to go – onmyway133 Jun 03 '15 at 08:31
  • 13
    If you're in IB, now is the time to remember the menu Editor -> Embed In -> View. Just select your UILabel first :) – Graham Perks Apr 26 '17 at 18:58
  • 1
    This is by far I see as the easiest solution. One just needs to make sure to align label in the center (horizontally and vertically) over the view and also update the view's bg color to match the bg color of your UILabel. – learner May 23 '21 at 03:13
  • If you need to update and resize the `UILabel` dynamically, create the `UIView` independently of the label (not as a super view), then Auto Layout will adjust `UIView` as the label resizes. – craft Jan 14 '22 at 19:38
  • The proposition is interesting, just to take into account that in UITesting a label will be inside an 'other' element, and secondly those constraints between label and container have 1000 priority so when compressing a view for small screens this can be something to hack – Blazej SLEBODA Dec 01 '22 at 19:02
58

SWIFT 4

Easy to use solution, available for all UILabel child in project.

Example:

let label = UILabel()
    label.<Do something>
    label.padding = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0)

UILabel Extension

import UIKit

extension UILabel {
    private struct AssociatedKeys {
        static var padding = UIEdgeInsets()
    }

    public var padding: UIEdgeInsets? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.padding) as? UIEdgeInsets
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(self, &AssociatedKeys.padding, newValue as UIEdgeInsets?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
    }

    override open func draw(_ rect: CGRect) {
        if let insets = padding {
            self.drawText(in: rect.inset(by: insets))
        } else {
            self.drawText(in: rect)
        }
    }

    override open var intrinsicContentSize: CGSize {
        guard let text = self.text else { return super.intrinsicContentSize }

        var contentSize = super.intrinsicContentSize
        var textWidth: CGFloat = frame.size.width
        var insetsHeight: CGFloat = 0.0
        var insetsWidth: CGFloat = 0.0

        if let insets = padding {
            insetsWidth += insets.left + insets.right
            insetsHeight += insets.top + insets.bottom
            textWidth -= insetsWidth
        }

        let newSize = text.boundingRect(with: CGSize(width: textWidth, height: CGFloat.greatestFiniteMagnitude),
                                        options: NSStringDrawingOptions.usesLineFragmentOrigin,
                                        attributes: [NSAttributedString.Key.font: self.font], context: nil)

        contentSize.height = ceil(newSize.size.height) + insetsHeight
        contentSize.width = ceil(newSize.size.width) + insetsWidth

        return contentSize
    }
}
32

We finally figured a full and correct solution which works in all cases, including stack views, dynamic cells, dynamic number of lines, collection views, animated padding, every character count, and every other situation.

Padding a UILabel, full solution. Updated for 2023.

It turns out there are three things that must be done.

1. Must call textRect#forBounds with the new smaller size

2. Must override drawText with the new smaller size

3. If a dynamically sized cell, must adjust intrinsicContentSize

In the typical example below, the text unit is in a table view, stack view or similar construction, which gives it a fixed width. In the example we want padding of 60,20,20,24.

Thus, we take the "existing" intrinsicContentSize and actually add 80 to the height.

To repeat ...

You have to literally "get" the height calculated "so far" by the engine, and change that value.

I find that process confusing, but, that is how it works. For me, Apple should expose a call named something like "preliminary height calculation".

Secondly we have to actually use the textRect#forBounds call with our new smaller size.

So in textRect#forBounds we first make the size smaller and then call super.

Alert! You must call super after, not before!

If you carefully investigate all the attempts and discussion on this page, that is the exact problem.

Notice some solutions "seem to usually work". This is indeed the exact reason - confusingly you must "call super afterwards", not before.

If you call super "in the wrong order", it usually works, but fails for certain specific text lengths.

Here is an exact visual example of "incorrectly doing super first":

enter image description here

Notice the 60,20,20,24 margins are correct BUT the size calculation is actually wrong, because it was done with the "super first" pattern in textRect#forBounds.

Fixed:

Only now does the textRect#forBounds engine know how to do the calculation properly:

enter image description here

Finally!

Again, in this example the UILabel is being used in the typical situation where width is fixed. So in intrinsicContentSize we have to "add" the overall extra height we want. (You don't need to "add" in any way to the width, that would be meaningless as it is fixed.)

Then in textRect#forBounds you get the bounds "suggested so far" by autolayout, you subtract your margins, and only then call again to the textRect#forBounds engine, that is to say in super, which will give you a result.

Finally and simply in drawText you of course draw in that same smaller box.

Phew!

let UIEI = UIEdgeInsets(top: 60, left: 20, bottom: 20, right: 24) // as desired

override var intrinsicContentSize:CGSize {
    numberOfLines = 0       // don't forget!
    var s = super.intrinsicContentSize
    s.height = s.height + UIEI.top + UIEI.bottom
    s.width = s.width + UIEI.left + UIEI.right
    return s
}

override func drawText(in rect:CGRect) {
    let r = rect.inset(by: UIEI)
    super.drawText(in: r)
}

override func textRect(forBounds bounds:CGRect,
                           limitedToNumberOfLines n:Int) -> CGRect {
    let b = bounds
    let tr = b.inset(by: UIEI)
    let ctr = super.textRect(forBounds: tr, limitedToNumberOfLines: 0)
    // that line of code MUST be LAST in this function, NOT first
    return ctr
}

Once again. Note that the answers on this and other QA that are "almost" correct suffer the problem in the first image above - the "super is in the wrong place". You must force the size bigger in intrinsicContentSize and then in textRect#forBounds you must first shrink the first-suggestion bounds and then call super.

Summary: you must "call super last" in textRect#forBounds

That's the secret.

Note that you do not need to and should not need to additionally call invalidate, sizeThatFits, needsLayout or any other forcing call. A correct solution should work properly in the normal autolayout draw cycle.

Fattie
  • 27,874
  • 70
  • 431
  • 719
  • 1
    Great solution. Could you please expand on why you are overwriting the `numberOfLines`? – DrMickeyLauer Dec 08 '20 at 17:47
  • 2
    thanks! it certainly took a lot of time to get to it :/ (in iOS, "numberOfLines zero" means wrap the text .. I simply set it in the example code, because, folks may forget to set it on storyboard!) – Fattie Dec 08 '20 at 19:02
  • I used this and the IBInspectable stuff from the highest-voted (but seemingly not as correct) answer (https://stackoverflow.com/a/32368958/826946) and added support for border (color, thickness, cornerRadius) also using IBInspectable. It's over here: https://stackoverflow.com/a/67317976/826946 – Andy Weinstein Apr 29 '21 at 13:09
  • 3
    Love how in 2021, you still aren't able to set a property on the `UILabel` to do this automatically. – JaredH May 12 '21 at 18:07
  • I have tried this approach, but I'm unable to dynamically set the font size afterwards? It seems to shrink to 10 despite me setting it to something much higher, like 24. And when I disable this code, the size is correct. I should add that I'm in a tableview with a cell and label that I think is having it's instrinsic size adjusted, by the padding and by linespacing. – Chucky Jun 29 '21 at 11:31
  • This did not work for me with text long enough to make it wrap to another line, at least when put into a SwiftUI stack, only one line is visible. – Jordan H Jul 10 '21 at 19:49
  • @JordanH - I'm surprised; it's not like intrinsic size etc. would be broken in SwiftUI eh! As mentioned to Chucky *"Very simply, add a print statement inside each of those three, particularly the first one, showing the size, and you'll quickly find out what's up."* Did you observe the "Don't forget" (search) lines in the code? I guess some simple problem is at hand ? – Fattie Jul 10 '21 at 20:05
  • Chucky - could it be that you simply forgot to set adjustsFontSizeToFitWidth correctly? Works perfectly in many projects for us. Jordan - it's very unlikely intrinsic size etc. would be broken in SwiftUI, did you catch the "don't forget" lines in the code? – Fattie Sep 11 '21 at 18:04
  • 1
    Unfortunately this overrides my `numberOfLines` setting. I only need to show 2 lines (and `...` after that), but this just sets it to 0 and showed all text instead. If I remove the `numberOfLines` setting, it shows the correct lines set, but the spacing is wrong. – CyberMew Mar 07 '22 at 07:28
  • @CyberMew ! the entire point of this is to do it when the text is any length! :) if you have a fixed size (two lines) - just set the size. For a trivial example, notice my other answer in this QA (which explains "here is a trivial solution if you just have a fixed size ...") Hope it helps! – Fattie Mar 08 '22 at 12:58
  • @Fattie I see your other answer that you warn works only for single line. But, where is the answer that works for 2 lines or any fixed number of lines? – Aswath Apr 11 '22 at 07:22
  • oh you want a fixed number of lines?? (like say "3" ?) I have no idea, that seems strange. – Fattie Apr 11 '22 at 20:57
21

Without Storyboard:

class PaddingLabel: UILabel {

    var topInset: CGFloat
    var bottomInset: CGFloat
    var leftInset: CGFloat
    var rightInset: CGFloat

    required init(withInsets top: CGFloat, _ bottom: CGFloat,_ left: CGFloat,_ right: CGFloat) {
        self.topInset = top
        self.bottomInset = bottom
        self.leftInset = left
        self.rightInset = right
        super.init(frame: CGRect.zero)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func drawText(in rect: CGRect) {
        let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

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

Usage:

let label = PaddingLabel(8, 8, 16, 16)
label.font = .boldSystemFont(ofSize: 16)
label.text = "Hello World"
label.backgroundColor = .black
label.textColor = .white
label.textAlignment = .center
label.layer.cornerRadius = 8
label.clipsToBounds = true
label.sizeToFit()

view.addSubview(label)

Result:

Alice Chan
  • 2,814
  • 2
  • 16
  • 16
20

Swift 4+

class EdgeInsetLabel: UILabel {
    var textInsets = UIEdgeInsets.zero {
        didSet { invalidateIntrinsicContentSize() }
    }

    override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        let invertedInsets = UIEdgeInsets(top: -textInsets.top,
                                          left: -textInsets.left,
                                          bottom: -textInsets.bottom,
                                          right: -textInsets.right)
        return textRect.inset(by: invertedInsets)
    }

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

Usage:

let label = EdgeInsetLabel()
label.textInsets = UIEdgeInsets(top: 2, left: 6, bottom: 2, right: 6)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
LE24
  • 355
  • 2
  • 5
  • WAIT - there's actually a problem I found with this in some cases. Previously this was the most correct answer. I've put in the correct answer below. – Fattie Nov 15 '19 at 11:53
  • ***I have included an image in my answer showing the problem*** – Fattie Nov 15 '19 at 12:01
15

Just use autolayout:

let paddedWidth = myLabel.intrinsicContentSize.width + 2 * padding
myLabel.widthAnchor.constraint(equalToConstant: paddedWidth).isActive = true

Done.

Tim Shadel
  • 1,487
  • 1
  • 15
  • 17
9

As per Swift 4.2 (Xcode 10 beta 6) "UIEdgeInsetsInsetRect" being deprecated. I've also declared the class public to make it more useful.

public class UIPaddedLabel: UILabel {

    @IBInspectable var topInset: CGFloat = 5.0
    @IBInspectable var bottomInset: CGFloat = 5.0
    @IBInspectable var leftInset: CGFloat = 7.0
    @IBInspectable var rightInset: CGFloat = 7.0

    public override func drawText(in rect: CGRect) {
        let insets = UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        super.drawText(in: rect.inset(by: insets))
    }

    public override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(width: size.width + leftInset + rightInset,
                      height: size.height + topInset + bottomInset)
    }

    public override func sizeToFit() {
        super.sizeThatFits(intrinsicContentSize)
    }
}
psyFi
  • 739
  • 7
  • 21
Stéphane de Luca
  • 12,745
  • 9
  • 57
  • 95
  • It works well. But I try to use it inside a CollectionViewCell and it doesn't resize well after reuse (event after sizeToFit and layoutIfNeeded). Any id how to resize it? – Bogy Sep 19 '18 at 08:25
  • 1
    I did update sizeToFit() to make it work with reusable view – Bogy Sep 20 '18 at 12:44
  • `sizeToFit()` should be public as: "Overriding instance method must be as accessible as its enclosing type" – psyFi Oct 23 '18 at 18:57
8

Swift 3 Code with Implementation Example

class UIMarginLabel: UILabel {

    var topInset:       CGFloat = 0
    var rightInset:     CGFloat = 0
    var bottomInset:    CGFloat = 0
    var leftInset:      CGFloat = 0

    override func drawText(in rect: CGRect) {
        let insets: UIEdgeInsets = UIEdgeInsets(top: self.topInset, left: self.leftInset, bottom: self.bottomInset, right: self.rightInset)
        self.setNeedsLayout()
        return super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }
}

class LabelVC: UIViewController {

    //Outlets
    @IBOutlet weak var labelWithMargin: UIMarginLabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        //Label settings.
        labelWithMargin.leftInset = 10
        view.layoutIfNeeded()
    }
}

Don't forget to add class name UIMarginLabel in storyboard label object. Happy Coding!

mriaz0011
  • 1,887
  • 23
  • 11
8

In Swift 3

best and simple way

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

}
SanRam
  • 936
  • 13
  • 13
7

I edited a little in the accepted answer. There is a problem when leftInset and rightInset increase, a part of text will be disappeared, b/c the width of label will be narrowed but the height does not increase as figure:

padding label with wrong intrinsic content size

To resolve this problem you need to re-calculate height of text as follow:

@IBDesignable class PaddingLabel: UILabel {

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

  override func drawTextInRect(rect: CGRect) {
    let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
    super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
  }

  override func intrinsicContentSize() -> CGSize {
    var intrinsicSuperViewContentSize = super.intrinsicContentSize()

    let textWidth = frame.size.width - (self.leftInset + self.rightInset)
    let newSize = self.text!.boundingRectWithSize(CGSizeMake(textWidth, CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: self.font], context: nil)
    intrinsicSuperViewContentSize.height = ceil(newSize.size.height) + self.topInset + self.bottomInset

    return intrinsicSuperViewContentSize
  }
}

and result:

padding label with right intrinsic content size

I hope to help some people in the same situation as me.

Quang Tran
  • 1,309
  • 13
  • 14
  • 2
    If you plan to use **Swift 3.0**, you must change function names, as new Apple language completely breaks previous func definition. So, `override func drawTextInRect(rect: CGRect)` becomes `override func drawText(in rect: CGRect)` and `override func intrinsicContentSize() -> CGSize` becomes `override var intrinsicContentSize : CGSize` Enjoy! – DoK Sep 30 '16 at 20:06
  • unfortunately I did not make it work. I tried with our code swift 5 `override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }` – Nazmul Hasan May 12 '20 at 23:47
7

Another option without subclassing would be to:

  1. Set label text
  2. sizeToFit()
  3. then increase label height a little to simulate padding

    label.text = "someText"
    label.textAlignment = .center    
    label.sizeToFit()  
    label.frame = CGRect( x: label.frame.x, y: label.frame.y,width:  label.frame.width + 20,height: label.frame.height + 8)
    
Joe
  • 8,868
  • 8
  • 37
  • 59
Guy
  • 1,254
  • 17
  • 16
  • Surprisingly, this was all I needed, just modified a little to this: `label.frame = CGRect( x: label.frame.origin.x - 10, y: label.frame.origin.y - 4, width: label.frame.width + 20,height: label.frame.height + 8)` the -10 and -4 for centralizing – Mofe Ejegi Feb 10 '18 at 06:01
7

Just like the other answers, but it fixes a bug:

When label.width is controlled by auto layout, sometimes text will be cropped.

@IBDesignable
class InsetLabel: UILabel {

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

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

    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 {
        let systemContentSize = super.intrinsicContentSize
        let adjustSize = CGSize(width: systemContentSize.width + leftInset + rightInset, height: systemContentSize.height + topInset +     bottomInset)
        if adjustSize.width > preferredMaxLayoutWidth && preferredMaxLayoutWidth != 0 {
            let constraintSize = CGSize(width: bounds.width - (leftInset + rightInset), height: .greatestFiniteMagnitude)
            let newSize = super.sizeThatFits(constraintSize)
            return CGSize(width: systemContentSize.width, height: ceil(newSize.height) + topInset + bottomInset)
        } else {
            return adjustSize
        }
    }

    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: insets))
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
PowHu
  • 2,159
  • 16
  • 17
  • An explanation would be in order. E.g., what is the idea/gist of the fix? What was changed or added? Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/53222793/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Nov 07 '21 at 11:38
  • this fails completely in all but the most trivial fixed cases – Fattie Aug 11 '22 at 14:30
6

Swift 3, iOS10 solution:

open class UIInsetLabel: UILabel {

    open var insets : UIEdgeInsets = UIEdgeInsets() {
        didSet {
            super.invalidateIntrinsicContentSize()
        }
    }

    open override var intrinsicContentSize: CGSize {
        var size = super.intrinsicContentSize
        size.width += insets.left + insets.right
        size.height += insets.top + insets.bottom
        return size
    }

    override open func drawText(in rect: CGRect) {
        return super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }
}
andrewz
  • 4,729
  • 5
  • 49
  • 67
4

Subclass UILabel. (File-New-File- CocoaTouchClass-make Subclass of UILabel).

//  sampleLabel.swift

import UIKit

class sampleLabel: UILabel {

 let topInset = CGFloat(5.0), bottomInset = CGFloat(5.0), leftInset = CGFloat(8.0), rightInset = CGFloat(8.0)

 override func drawTextInRect(rect: CGRect) {

  let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
  super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))

 }
 override func intrinsicContentSize() -> CGSize {
  var intrinsicSuperViewContentSize = super.intrinsicContentSize()
  intrinsicSuperViewContentSize.height += topInset + bottomInset
  intrinsicSuperViewContentSize.width += leftInset + rightInset
  return intrinsicSuperViewContentSize
 }
}

On ViewController:

override func viewDidLoad() {
  super.viewDidLoad()

  let labelName = sampleLabel(frame: CGRectMake(0, 100, 300, 25))
  labelName.text = "Sample Label"
  labelName.backgroundColor =  UIColor.grayColor()

  labelName.textColor = UIColor.redColor()
  labelName.shadowColor = UIColor.blackColor()
  labelName.font = UIFont(name: "HelveticaNeue", size: CGFloat(22))
  self.view.addSubview(labelName)
 }

OR Associate custom UILabel class on Storyboard as Label's class.

Alvin George
  • 14,148
  • 92
  • 64
  • i would up vote if you change those hardcoded values into class properties, i am already using this code. – Juan Boero Apr 08 '16 at 21:43
  • @ Juan : drawTextInRect is a default class property of UILabel which we are unable to override using code. The best practice to subclass UILabel and add required frame change. Anyhow, it is convenient as Inheritance feature. – Alvin George Apr 11 '16 at 06:16
  • this is correct, however as of Swift 3 at least, intrinsicContentSize is not a function but rather a property, so should be "override var intrinsicContentSize: CGFloat {}" instead of "override func intrinsicContentSize", just a note. – jjjjjjjj Jun 09 '17 at 07:05
4

An elaboration on Mundi's answer.

I.e., embedding a label in a UIView and enforcing padding through Auto Layout. Example:

It looks like a padded UILabel

Overview:

  1. Create a UIView ("panel"), and set its appearance.

  2. Create a UILabel and add it to the panel.

  3. Add constraints to enforce padding.

  4. Add the panel to your view hierarchy, and then position the panel.

Details:

  1. Create the panel view.

    let panel = UIView() panel.backgroundColor = .green panel.layer.cornerRadius = 12

  2. Create the label, add it to the panel as a subview.

    let label = UILabel() panel.addSubview(label)

  3. Add constraints between the edges of the label and the panel. This forces the panel to keep a distance from the label. I.e., "padding".

Editorial: doing all this by hand is super-tedious, verbose and error-prone. I suggest you pick an Auto Layout wrapper from GitHub or write one yourself

label.panel.translatesAutoresizingMaskIntoConstraints = false
label.topAnchor.constraint(equalTo: panel.topAnchor,
    constant: vPadding).isActive = true
label.bottomAnchor.constraint(equalTo: panel.bottomAnchor,
    constant: -vPadding).isActive = true
label.leadingAnchor.constraint(equalTo: panel.leadingAnchor,
    constant: hPadding).isActive = true
label.trailingAnchor.constraint(equalTo: panel.trailingAnchor,
    constant: -hPadding).isActive = true

label.textAlignment = .center
  1. Add the panel to your view hierarchy and then add positioning constraints. E.g., hug the right-hand side of a tableViewCell, as in the example image.

Note: you only need to add positional constraints, not dimensional constraints: Auto Layout will solve the layout based on both the intrinsicContentSize of the label and the constraints added earlier.

hostView.addSubview(panel)
panel.translatesAutoresizingMaskIntoConstraints = false
panel.trailingAnchor.constraint(equalTo: hostView.trailingAnchor,
    constant: -16).isActive = true
panel.centerYAnchor.constraint(equalTo: hostView.centerYAnchor).isActive = true
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Womble
  • 4,607
  • 2
  • 31
  • 45
3

Use this code if you are facing a text trimming problem while applying padding.

@IBDesignable class PaddingLabel: UILabel {

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

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

    override var intrinsicContentSize: CGSize {
        var intrinsicSuperViewContentSize = super.intrinsicContentSize
        let textWidth = frame.size.width - (self.leftInset + self.rightInset)
        let newSize = self.text!.boundingRect(with: CGSize(textWidth, CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: self.font], context: nil)
        intrinsicSuperViewContentSize.height = ceil(newSize.size.height) + self.topInset + self.bottomInset
        return intrinsicSuperViewContentSize
    }
}

extension CGSize{
    init(_ width:CGFloat,_ height:CGFloat) {
        self.init(width:width,height:height)
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ceekay
  • 1,136
  • 13
  • 11
  • 1
    Thank you for posting, I am looking for a solution regardin padding + trimming. It seems to me your solution breaks label.numberOfLines = 0 which I need. Any workaround? – Don Oct 05 '18 at 08:38
  • @Don the only correct solution in all cases: https://stackoverflow.com/a/58876988/294884 – Fattie Feb 26 '21 at 13:36
3

Swift 5 Example with UILabel Extension

With the code below setting your margins is as easy as label.setMargins(15).

extension UILabel {
    func setMargins(_ margin: CGFloat = 10) {
        if let textString = self.text {
            let 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
        }
    }
}
craft
  • 2,017
  • 1
  • 21
  • 30
Zorayr
  • 23,770
  • 8
  • 136
  • 129
3

Strictly for SINGLE-LINE labels: (2021 syntax)

For anyone googling here who needs padding on a STRICTLY SINGLE LINE label (such as a section heading or other list item),

The syntax has changed a lot. Beware out of date info on the internet.

Here's the exact class to copy and paste:

// add 100 above, 50 padding below a SINGLE-LINE label
import UIKit
class SingleLineLabelWithSpacing: UILabel {
    // STRICTLY for SINGLE LINE labels
    // only works with SINGLE LINE labels

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

    override var intrinsicContentSize: CGSize {
        var ic = super.intrinsicContentSize
        ic.height = ic.height + 150
        return ic
    }
}

enter image description here

In the example, padding above/below of 100/50.

This is the usual thing to do when you have any sort of scrolling list, feed, or other list.

In this way you never have to think about the spacing above/below the headline, username, etc - you just drop it in the stack view or whatever the case is.

Also of course you can change the two values everywhere all at once when the designers want to tweak it.

Reminder: if you want to truly pad a UILabel so that it works perfectly regardless of number of lines of text, dynamic sizing cells, animations, etc etc etc, it is very complicated. The only correct answer is: https://stackoverflow.com/a/58876988/294884

Fattie
  • 27,874
  • 70
  • 431
  • 719
3

If you want to use UILabel

class UILabel : UIKit.UILabel {
    var insets = UIEdgeInsets.zero {
        didSet { invalidateIntrinsicContentSize() }
    }

    override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        let invertedInsets = UIEdgeInsets(top: -insets.top,
                                          left: -insets.left,
                                          bottom: -insets.bottom,
                                          right: -insets.right)
        return textRect.inset(by: invertedInsets)
    }

    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: insets))
    }
}
Abdurakhmon
  • 2,813
  • 5
  • 20
  • 41
2

Easy padding (Swift 3.0, Alvin George answer):

  class NewLabel: UILabel {

        override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
                return self.bounds.insetBy(dx: CGFloat(15.0), dy: CGFloat(15.0))
        }

        override func draw(_ rect: CGRect) {
                super.drawText(in: self.bounds.insetBy(dx: CGFloat(5.0), dy: CGFloat(5.0)))
        }

  }
odemolliens
  • 2,581
  • 2
  • 32
  • 34
2

One pragmatic solution is to add blank labels of the same height and color as the main label. Set the leading/trailing space to the main label to zero, align vertical centers, and make the width your desired margin.

pks1981
  • 21
  • 2
2

If you don't want or need to use an @IBInspectable / @IBDesignable UILabel in Storyboard (I think those are rendered too slow anyway), then it is cleaner to use UIEdgeInsets instead of 4 different CGFloats.

Code example for Swift 4.2:

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

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

    public override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(width: size.width + padding.left + padding.right,
                      height: size.height + padding.top + padding.bottom)
    }
}
Simon Backx
  • 1,282
  • 14
  • 16
2

My solution is similar to what people answered but adds sizeThatFits to help UIKit to figure out the right size.

class InsetLabel : UILabel {
    @objc var textInsets: UIEdgeInsets = .zero

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

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var s = super.sizeThatFits(CGSize(width: size.width - (textInsets.left + textInsets.right), height: size.height - (textInsets.top + textInsets.bottom)))
        s.height += textInsets.top + textInsets.bottom
        return s
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Cherpak Evgeny
  • 2,659
  • 22
  • 29
1

Similar to other answers, but with a func class to setup the padding dinamically:

class UILabelExtendedView: UILabel
{
    var topInset: CGFloat = 4.0
    var bottomInset: CGFloat = 4.0
    var leftInset: CGFloat = 8.0
    var rightInset: CGFloat = 8.0

    override func drawText(in rect: CGRect)
    {
        let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

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

    func setPadding(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat){
        self.topInset = top
        self.bottomInset = bottom
        self.leftInset = left
        self.rightInset = right
        let insets: UIEdgeInsets = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
        super.drawText(in: UIEdgeInsetsInsetRect(self.frame, insets))
    }
}
Pelanes
  • 3,451
  • 1
  • 34
  • 32
1

Objective-C

Based on Tai Le's answer, which implements the feature inside an Interface Builder Designable, here's the Objective-C version.

Put this in YourLabel.h file:

@interface YourLabel : UILabel

@property IBInspectable CGFloat topInset;
@property IBInspectable CGFloat bottomInset;
@property IBInspectable CGFloat leftInset;
@property IBInspectable CGFloat rightInset;

@end

And this would go in file YourLabel.m:

IB_DESIGNABLE

@implementation YourLabel

#pragma mark - Super

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        self.topInset = 0;
        self.bottomInset = 0;
        self.leftInset = 0;
        self.rightInset = 0;
    }
    return self;
}

- (void)drawTextInRect:(CGRect)rect {
    UIEdgeInsets insets = UIEdgeInsetsMake(self.topInset, self.leftInset, self.bottomInset, self.rightInset);
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}

- (CGSize)intrinsicContentSize {
    
    CGSize size = [super intrinsicContentSize];
    return CGSizeMake(size.width + self.leftInset + self.rightInset,
                      size.height + self.topInset + self.bottomInset);
}

@end

You can then modify YourLabel insets directly in Interface Builder after specifying the class inside the XIB file or storyboard, the default value of the insets being zero.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lmarceau
  • 66
  • 4
1

I haven’t seen this answer given anywhere. My technique is to set a width constraint on the label and adjust that width when setting the label text.

self.myLabel.text = myString;

UIFont * const font = [UIFont systemFontOfSize:17 weight:UIFontWeightRegular]; // Change to your own label font.

CGSize const size = CGSizeMake(INFINITY, 18); // 18 is height of label.

CGFloat const textWidth = [myString boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: font} context:nil].size.width;

self.myLabelWidthConstraint.constant = textWidth + 20; // 10 padding on each side. Also, set text alignment to centre.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0

Easy way

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.view.addSubview(makeLabel("my title",x: 0, y: 100, w: 320, h: 30))
    }

    func makeLabel(title:String, x:CGFloat, y:CGFloat, w:CGFloat, h:CGFloat)->UILabel{
        var myLabel : UILabel = UILabel(frame: CGRectMake(x,y,w,h))
        myLabel.textAlignment = NSTextAlignment.Right

        // inser last char to right
        var titlePlus1char = "\(title)1"
        myLabel.text = titlePlus1char
        var titleSize:Int = count(titlePlus1char)-1

        myLabel.textColor = UIColor(red:1.0, green:1.0,blue:1.0,alpha:1.0)
        myLabel.backgroundColor = UIColor(red: 214/255, green: 167/255, blue: 0/255,alpha:1.0)


        // create myMutable String
        var myMutableString = NSMutableAttributedString()

        // create myMutable font
        myMutableString = NSMutableAttributedString(string: titlePlus1char, attributes: [NSFontAttributeName:UIFont(name: "HelveticaNeue", size: 20)!])

        // set margin size
        myMutableString.addAttribute(NSFontAttributeName, value: UIFont(name: "HelveticaNeue", size: 10)!, range: NSRange(location: titleSize,length: 1))

        // set last char to alpha 0
        myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor(red:1.0, green:1.0,blue:1.0,alpha:0), range: NSRange(location: titleSize,length: 1))

        myLabel.attributedText = myMutableString

        return myLabel
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}
Shashank Agarwal
  • 2,769
  • 1
  • 22
  • 24
0

Easy padding:

import UIKit

    class NewLabel: UILabel {

        override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {

            return CGRectInset(self.bounds, CGFloat(15.0), CGFloat(15.0))
        }

        override func drawRect(rect: CGRect) {

            super.drawTextInRect(CGRectInset(self.bounds,CGFloat(5.0), CGFloat(5.0)))
        }

    }
Alvin George
  • 14,148
  • 92
  • 64
0

If you want to add 2px padding around the textRect, just do this:

let insets = UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2)
label.frame = UIEdgeInsetsInsetRect(textRect, insets)
zs2020
  • 53,766
  • 29
  • 154
  • 219
0

Swift 4+

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 10

// Swift 4.2++
label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])

// Swift 4.1--
label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedStringKey.paragraphStyle: paragraphStyle])
-1

Swift 5

On the method layoutSubviews() change the inserts of your UILabel as bellow.

  override func layoutSubviews() {
    super.layoutSubviews()

    label.frame = label.frame.inset(by: UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10))
  }
  • @Fattie It works for me and it may work for someone else if other people are suggesting the same answer. – Kivia Martins Brito May 04 '20 at 19:05
  • Ciao Kivia, I'm afraid it honestly does not work - it only works in the simplest test examples where it's not part of a layout, the label is not constrained in any way to other elements around it, and the text is not set on the fly. iOS is a pain!!!! – Fattie May 05 '20 at 13:39
-5

If you're looking for just left and right padding, you can simply add empty spaces before and after the text:

titleLabel.text = " \(categoryName) "

enter image description here

enter image description here

enter image description here

fullmoon
  • 8,030
  • 5
  • 43
  • 58
  • 1
    This does not work. iOS actually *removes* any trailing spaces. here's a discussion about it: https://stackoverflow.com/a/59813420/294884 – Fattie May 03 '20 at 17:27
  • 1
    It works man, please see the updated answer and change your down vote – fullmoon May 09 '20 at 12:00
  • Badr, *it indeed removes TRAILING spaces*, Means, at the end of the string. `= " \(categoryName) "` .. try it – Fattie May 09 '20 at 20:19
  • 1
    It still works, I updated the answer. It probably not the exact number of empty spaces, but leading and trailing spaces are working in my case. – fullmoon May 09 '20 at 20:56