[Swift] Set the margins for multiple lines of UI Label: top, bottom, left, and right

Introduction

The environment is ・ Xcode 11.6 ・ Swift 5 Will be.

There are times when you want to set the label margins (padding), right?

For example, in the case of textView, use textContainerInset, and in the case of UIButton, use contentEdgeInsets, etc., and I think that it can be set relatively easily.

But with UILabel, it's a bit annoying, especially with multi-line text.

Try first

Especially for the horizontal margins, ʻattributedText` may solve it neatly. The following is an example.

let style = NSMutableParagraphStyle()
// horizontal setting
style.headIndent = 0
style.tailIndent = 0
// vertical setting
style.lineSpacing = 0
style.maximumLineHeight = 16
style.minimumLineHeight = 16
style.paragraphSpacingBefore = 10
style.paragraphSpacing = 30

let attr: [NSAttributedString.Key : Any] = [
    .font: ...,
    .paragraphStyle : style,
]
let attributedText = NSAttributedString(string: "hoge", attributes: attr)

let label = UILabel()
label.attributedText = attributedText

In the vertical direction, you can specify the line spacing and line height, but it's a shame that you can't set the margins directly. However, if the text is one line, you can set the line height and font size well to effectively control the top and bottom margins.

Usually this is okay

It's a common way to search, but if you create a custom Label that inherits UILabel as shown below, it will usually be solved.

You can set the top, bottom, left, and right margins properly.


class PaddingLabel: UILabel {

    var padding: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

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

    override var intrinsicContentSize: CGSize {
        var contentSize = super.intrinsicContentSize
        contentSize.height += padding.top + padding.bottom
        contentSize.width += padding.left + padding.right
        return contentSize
    }
    
    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var contentSize = super.sizeThatFits(size)
        contentSize.width += padding.left + padding.right
        contentSize.height += padding.top + padding.bottom
        return contentSize
    }
}

You don't have to have sizeThatFits (_ size: CGSize) here, but if you do, it will automatically adjust to the size including the margins when you do sizeToFit (), so I'm grateful. Of course, if you don't want to include margins in sizeToFit (), don't override it.

Last resort

If you have a problem that doesn't work, the last resort is probably

UILabel( ) in UIView( )

As a label, you can treat the parent view as a label (or button) with margins by placing it at any position in the parent view after making it blank with the usual sizeToFit ().

In this case you have to specify the size of the parent view here, but you should be able to semi-automate with sizeToFit () + ʻAutoLayout`.

Finally

Please let me know if there is another good way!

Recommended Posts

[Swift] Set the margins for multiple lines of UI Label: top, bottom, left, and right
Automatically set the width and height of the UI Label
Set the number of seconds for fast forward and rewind in ExoPlayer
Compare the speed of the for statement and the extended for statement.