在UILabel中添加空格/填充

264

我有一个UILabel,想在顶部和底部添加间距。通过将最小高度约束进行修改,我已经实现了:

Enter image description here

为此,我使用了以下代码:

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))
}
但我必须找到一种不同的方法,因为如果我写超过两行,问题就会相同:

Enter image description here


我们终于终于弄清楚了如何在所有动态情况下完美地替换UILabel,而无需重新布局或任何其他问题。太好了。 - Fattie
38个回答

15

只需使用自动布局:

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

完成。


1
你也可以用同样的方法来处理高度。 - ShadeToD

9
根据Swift 4.2(Xcode 10 beta 6)的规定,“UIEdgeInsetsInsetRect”已被弃用。我还将该类声明为public,以使其更加有用。
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)
    }
}

它的功能很好。但是我试图在CollectionViewCell中使用它,重用后它无法很好地重新调整大小(即使进行了sizeToFit和layoutIfNeeded)。有没有什么方法可以重新调整大小? - Bogy
1
我已经更新了sizeToFit(),使其能够与可重用视图一起使用。 - Bogy
sizeToFit()应该声明为公有的,因为:「重写的实例方法必须和它所在的类型具有相同的访问级别。」 - psyFi

8

使用示例的Swift 3代码实现

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()
    }
}

不要忘记在故事板中的标签对象中添加类名UIMarginLabel。 编码愉快!

8

与其他答案类似,但它修复了一个错误:

label.width由自动布局控制时,有时文本会被裁剪。

@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
这在除了最简单的固定情况之外完全失败。 - Fattie

8

在Swift 3中,最好且简单的方法是:

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))
    }

}

请问您如何将此代码用于UILabel? - David_2877
嗨@David_2877,这是一个类,只需将此类添加到您的项目中,当您想要使用UILabel时,请使用UILabelPadded。如果您需要更多帮助,请告诉我。 - SanRam

7

我对被接受的答案进行了一些修改。当 leftInsetrightInset 增加时,文本的一部分将会消失,因为标签的宽度会变窄,但高度不会增加,如下图所示:

padding label with wrong intrinsic content size

要解决这个问题,你需要按照以下方式重新计算文本的高度:

@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
  }
}

并且结果为:

具有正确内在内容大小的填充标签

我希望能帮助与我处境相同的人。


2
如果您计划使用 Swift 3.0,则必须更改函数名称,因为新的苹果语言完全破坏了以前的函数定义。因此,override func drawTextInRect(rect: CGRect) 变成了 override func drawText(in rect: CGRect),而 override func intrinsicContentSize() -> CGSize 则变成了 override var intrinsicContentSize : CGSize祝您愉快! - DoK
不幸的是,我没有让它工作。我尝试使用我们的 Swift 5 代码 override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize } - Nazmul Hasan

7

在不使用子类来完成的情况下,另一个选择是:

  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)
    

令人惊讶的是,这就是我所需要的全部内容,只需对其进行少量修改即可: 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) 其中的-10和-4用于居中对齐。 - Mofe Ejegi

6

解决Swift 3、iOS10的方案:

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))
    }
}

4

继承UILabel类。(文件-新建文件-CocoaTouchClass-将子类设置为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
 }
}

在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)
 }

在Storyboard中,将自定义的UILabel类关联到Label的class上。


如果你把那些硬编码的值改成类属性,我会点赞的,因为我已经在使用这段代码了。 - Juan Boero
@ Juan:drawTextInRect是UILabel的默认类属性,我们无法使用代码覆盖它。最佳实践是子类化UILabel并添加所需的框架更改。不管怎样,这是继承特性很方便。 - Alvin George
这是正确的,但至少在Swift 3中,intrinsicContentSize不是一个函数而是一个属性,因此应该使用"override var intrinsicContentSize: CGFloat {}"而不是"override func intrinsicContentSize",只是一条注释。 - jjjjjjjj

4

Mundi的回答的进一步阐述。

即,在UIView中嵌入标签,并通过自动布局强制填充。例如:

It looks like a padded UILabel

概述:

  1. 创建一个 UIView("面板"),并设置其外观。

  2. 创建一个 UILabel 并将其添加到面板中。

  3. 添加约束以强制填充。

  4. 将面板添加到您的视图层次结构中,然后定位面板。

详细信息:

  1. 创建面板视图。

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

  2. 创建标签,将其作为子视图添加到面板中。

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

  3. 在标签和面板的边缘之间添加约束。这可以强制面板与标签保持一定距离。即“填充”。

编辑:手动完成所有这些工作非常乏味、冗长且容易出错。我建议您从 GitHub 上选择一个自动布局包装器或编写自己的包装器。

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. 将面板添加到您的视图层次结构中,然后添加定位约束。例如,像示例图像中一样紧贴tableViewCell的右侧。

注意:您只需要添加位置约束,而不是尺寸约束:Auto Layout将根据标签的intrinsicContentSize和先前添加的约束来解决布局问题。

hostView.addSubview(panel)
panel.translatesAutoresizingMaskIntoConstraints = false
panel.trailingAnchor.constraint(equalTo: hostView.trailingAnchor,
    constant: -16).isActive = true
panel.centerYAnchor.constraint(equalTo: hostView.centerYAnchor).isActive = true

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接