UILabel 文字增加内边距,长度自适应

2,541 阅读2分钟

昨天UI还原的时候,UI小姐姐提出一个显示状态信息的label上,文字的左右应该留有4像素的间距,并且状态改变后文字内容也跟随改变,但label左右的内边距应该保持不变,且宽度自定适配

1721700183030.jpg

垃圾实现方案

手动在文字两边加一个或两个空格,然后根据文字的font信息计算出label所占用的宽度。这是我第一反应给出的实现,但是两边的空格信息是没有处理的很严格,一个或两个空格的宽度跟具体留白4像素或者8像素不是一回事,而且还需要计算一遍宽度信息。

后面又给出了另一种方案,在label的外面套一层view,label的左右留白设置到距离view的左右边距。可以完美还原UI给的留白间距了,但是需要多一个view的开销,且计算label的宽度信息变得更复杂

自定义UILabel实现

重写textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect#draw​Text(in:)方法

# text​Rect(for​Bounds:)

Returns the drawing rectangle for the text field’s text.

# draw​Text(in:)

Draws the label’s text, or its shadow, in the specified rectangle.

这两个方法会把text绘制到指定的范围并且返回text的CGRect信息。在forBounds中重新设施rect的width和height,这是因为rect是label的文字需要完全显示的大小,给label指定了内边距后会让text绘制后显示不完全,需要手动宽展因为设置的内边距带来的rect的变化。拿到rect是通过bounds.inset(by: self.contentInset)方法,具体实现逻辑如下

override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
    var rect = bounds.inset(by: self.contentInset)
    rect.size = super.textRect(forBounds: rect, limitedToNumberOfLines: numberOfLines).size
    rect.size.width += self.contentInset.left + self.contentInset.right
    return rect
}

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

contentInset是我给自定义的label设置的一个UIEdgeInsets属性,调用者传入具体的值就可以设置label中text的留白信息了。完整的demo如下:

class StatusLabel: UILabel {
    var contentInset: UIEdgeInsets
    
    init(contentInset: UIEdgeInsets) {
        
        self.contentInset = contentInset
        super.init(frame: .zero)
        setupViews()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setupViews() {
        // 一些可有可无的样式设置
        self.font = CustomFont.bodySmall.font()
        self.textAlignment = .center
        self.layer.cornerRadius = 3
        self.layer.masksToBounds = true
    }
    
    func updateStatus(status: Int, title: String? = nil) {
        if status == 0 {
            // 一些颜色、背景色的设置
        } else {
            // 一些颜色、背景色的设置
        }
        if title != nil {
            self.text = title
        }
        self.sizeToFit()
    }
    
    
    override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
        var rect = bounds.inset(by: self.contentInset)
        rect.size = super.textRect(forBounds: rect, limitedToNumberOfLines: numberOfLines).size
        rect.size.width += self.contentInset.left + self.contentInset.right
        return rect
    }
    
    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: self.contentInset))
    }
    
}

let statusLabel = StatusLabel(contentInset: UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 4))