昨天UI还原的时候,UI小姐姐提出一个显示状态信息的label上,文字的左右应该留有4像素的间距,并且状态改变后文字内容也跟随改变,但label左右的内边距应该保持不变,且宽度自定适配
垃圾实现方案
手动在文字两边加一个或两个空格,然后根据文字的font信息计算出label所占用的宽度。这是我第一反应给出的实现,但是两边的空格信息是没有处理的很严格,一个或两个空格的宽度跟具体留白4像素或者8像素不是一回事,而且还需要计算一遍宽度信息。
后面又给出了另一种方案,在label的外面套一层view,label的左右留白设置到距离view的左右边距。可以完美还原UI给的留白间距了,但是需要多一个view的开销,且计算label的宽度信息变得更复杂
自定义UILabel实现
重写textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect和#drawText(in:)方法
# textRect(forBounds:)
Returns the drawing rectangle for the text field’s text.
# drawText(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))