How do I create padding around the text of a UILabel?

Viewed 4296

I'm styling a UILabel programmatically and having trouble getting any padding around the label's text:

enter image description here

After studying many SO threads on the subject (this one, in particular) I thought I'd found a solution but it has no affect on the label. I've created a custom class for the label that subclasses UILabel:

// Swift 3 (I know, I know...)

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

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

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

And I invoke it like so:

func setPubTypeBadge(publication: PublicationModel, indexPathRow: Int) {
    if let pubType = publication.publicationType,
        let pubEnum = PublicationType(rawValue: pubType) {
        pubTypeBadge.attributedText = pubEnum.getBadgeLabel(using: pubType)
    }

    let rect = pubTypeBadge.textRect(forBounds: pubTypeBadge.frame, limitedToNumberOfLines: 1)
    pubTypeBadge.drawText(in: rect)
    pubTypeBadge.layer.borderColor = UIColor.dsShade3.cgColor
    pubTypeBadge.layer.borderWidth = 1
    pubTypeBadge.layer.cornerRadius = 4
}

which does absolutely nothing as far as the padding/insets are concerned. I simplly want 6pts of space on the left/right between the text and the border and 4pts on the top/bottom. Any help greatly appreciated.

2 Answers

The code below does what you want (Create new LiveView playground and paste it).

But I provide it just for the sake of demonstration and consider that placing UILabel in the container and making proper constraints will be a proper solution for this task.

import UIKit import PlaygroundSupport

class MyViewController : UIViewController {
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let label = BorderedLabel()
        label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
        label.text = "Hello World!"
        label.textColor = .black
        // Border settings
        label.layer.cornerRadius = 4 
        label.layer.borderColor = UIColor.black.cgColor
        label.layer.borderWidth = 1
        label.sizeToFit() // Important

        view.addSubview(label)
        self.view = view
    }
}

class BorderedLabel: UILabel {
    var sidePadding = CGFloat(10) // Needed padding, add property observers

    override func sizeToFit() {
        super.sizeToFit()
        bounds.size.width += 2 * sidePadding
    }

    override func drawText(in rect: CGRect) {
        print(rect)
        super.drawText(in: rect.insetBy(dx: sidePadding, dy: 0))
        invalidateIntrinsicContentSize()
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

However you can play with this solution and test it or improve convenience and reliability (along with Auto Layout support)

You can just add empty space before and after the text:

titleLabel.text = " \(itemName) "

Related