我有一个UILabel,我想在顶部和底部添加空格。在限制最小高度的情况下,我将其修改为:

为了做到这一点,我使用了:

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

但我必须找到不同的方法,因为如果我写了超过两行,问题是一样的:


当前回答

斯威夫特 4+

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 10

// Swift 4.2++
label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])

// Swift 4.1--
label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedStringKey.paragraphStyle: paragraphStyle])

其他回答

类似于其他答案,但使用func类来动态地设置填充:

class UILabelExtendedView: UILabel
{
    var topInset: CGFloat = 4.0
    var bottomInset: CGFloat = 4.0
    var leftInset: CGFloat = 8.0
    var rightInset: CGFloat = 8.0

    override func drawText(in rect: CGRect)
    {
        let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

    override public var intrinsicContentSize: CGSize
    {
        var contentSize = super.intrinsicContentSize
        contentSize.height += topInset + bottomInset
        contentSize.width += leftInset + rightInset
        return contentSize
    }

    func setPadding(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat){
        self.topInset = top
        self.bottomInset = bottom
        self.leftInset = left
        self.rightInset = right
        let insets: UIEdgeInsets = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
        super.drawText(in: UIEdgeInsetsInsetRect(self.frame, insets))
    }
}

我们最终找到了一个完整而正确的解决方案,它适用于所有情况,包括堆栈视图、动态单元格、动态行数、集合视图、动画填充、每个字符计数和所有其他情况。

填充一个UILabel,完整的解决方案。更新至2021年。

事实证明,有三件事是必须做的。

1. 必须调用textrect# forBounds与新的更小的大小

2. 必须重写drawText与新的更小的大小

3.如果是动态大小的单元格,必须调整intrinsicContentSize

在下面的典型示例中,文本单元位于表视图、堆栈视图或类似结构中,这给了它一个固定的宽度。在这个例子中,我们想要填充为60,20,20,24。

因此,我们使用“现有的”intrinsicContentSize并在高度上实际添加80。

重复一遍……

你必须从字面上“获得”引擎“到目前为止”计算的高度,并更改该值。

我觉得这个过程很混乱,但是,这就是它的工作原理。对我来说,苹果应该公开一个名为“初步高度计算”之类的调用。

其次,我们必须实际使用textrect# forBounds调用与我们的新小尺寸。

因此,在textrect# forBounds中,我们首先将大小减小,然后调用super。

警报!你必须在之后打电话给管理员,而不是在之前!

如果你仔细研究本页上的所有尝试和讨论,这就是问题所在。

注意一些解决方案“似乎通常有效”。这确实是确切的原因——令人困惑的是,你必须“事后打电话给超级”,而不是之前。

如果你“以错误的顺序”调用super,它通常会工作,但对于某些特定的文本长度会失败。

下面是一个“错误地先做超级”的直观例子:

注意,60,20,20,24边距是正确的,但大小计算实际上是错误的,因为它是用textrect# forBounds中的“super first”模式完成的。

修复:

只有现在textrect# forBounds引擎才知道如何正确地进行计算:

终于!

同样,在这个例子中,UILabel是在宽度固定的典型情况下使用的。因此,在intrinsicContentSize中,我们必须“添加”我们想要的整体额外高度。(你不需要以任何方式“添加”宽度,这将是没有意义的,因为它是固定的。)

然后在textrect# forBounds中,你通过自动布局得到了“建议到目前为止”的边界,你减去你的边距,然后才再次调用textrect# forBounds引擎,也就是说在super中,这将给你一个结果。

最后,在drawText中,你当然可以在同样小的方框中绘制。

唷!

let UIEI = UIEdgeInsets(top: 60, left: 20, bottom: 20, right: 24) // as desired

override var intrinsicContentSize:CGSize {
    numberOfLines = 0       // don't forget!
    var s = super.intrinsicContentSize
    s.height = s.height + UIEI.top + UIEI.bottom
    s.width = s.width + UIEI.left + UIEI.right
    return s
}

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

override func textRect(forBounds bounds:CGRect,
                           limitedToNumberOfLines n:Int) -> CGRect {
    let b = bounds
    let tr = b.inset(by: UIEI)
    let ctr = super.textRect(forBounds: tr, limitedToNumberOfLines: 0)
    // that line of code MUST be LAST in this function, NOT first
    return ctr
}

再一次。请注意,关于这个问题和其他“几乎”正确的QA的答案会遇到上面第一张图中的问题——“super在错误的地方”。你必须在intrinsicContentSize中强制size更大,然后在textrect# forBounds中,你必须首先缩小第一个建议的边界,然后调用super。

总结:你必须在textrect# forBounds中“调用super last”

这就是秘诀。

请注意,您不需要也不应该需要额外调用invalidate、sizeThatFits、needsLayout或任何其他强制调用。正确的解决方案应该在正常的自动布局绘制周期中正常工作。

只需使用一个UIView作为一个superview,并定义一个固定的边距标签与自动布局。

简单填充(Swift 3.0, Alvin George回答):

  class NewLabel: UILabel {

        override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
                return self.bounds.insetBy(dx: CGFloat(15.0), dy: CGFloat(15.0))
        }

        override func draw(_ rect: CGRect) {
                super.drawText(in: self.bounds.insetBy(dx: CGFloat(5.0), dy: CGFloat(5.0)))
        }

  }

严格适用于单行标签:(2021语法)

对于任何谷歌这里谁需要在严格单行标签填充(如节标题或其他列表项),

语法发生了很大变化。当心互联网上的过时信息。

下面是要复制和粘贴的确切类:

// add 100 above, 50 padding below a SINGLE-LINE label
import UIKit
class SingleLineLabelWithSpacing: UILabel {
    // STRICTLY for SINGLE LINE labels
    // only works with SINGLE LINE labels

    override func drawText(in rect: CGRect) {
        let insets: UIEdgeInsets = UIEdgeInsets(
               top: 100, left: 0, bottom: 50, right: 0)
        super.drawText(in: rect.inset(by: insets))
    }

    override var intrinsicContentSize: CGSize {
        var ic = super.intrinsicContentSize
        ic.height = ic.height + 150
        return ic
    }
}

在这个例子中,填充大于或小于100/50。

当您有任何类型的滚动列表、提要或其他列表时,这是通常要做的事情。

这样你就不必考虑标题上方/下方的空格,用户名等等——你只需要把它放到堆栈视图或其他任何情况下。

当然,当设计师想要调整时,你也可以在任何地方同时更改这两个值。

提醒:如果你想真正填充一个UILabel,让它无论文本行数、动态大小单元格、动画等等都能完美工作,这是非常复杂的。唯一正确的答案是:https://stackoverflow.com/a/58876988/294884