我有一个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))
}

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


当前回答

简单填充(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)))
        }

  }

其他回答

我的解决方案与人们回答的类似,但添加了sizeThatFits来帮助UIKit找出正确的大小。

class InsetLabel : UILabel {
    @objc var textInsets: UIEdgeInsets = .zero

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

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

如果你想坚持使用UILabel,而不子类化它,Mundi已经给了你一个明确的解决方案。

如果你想避免用UIView来包装UILabel,你可以使用UITextView来支持UIEdgeInsets (padding)或者子类UILabel来支持UIEdgeInsets。

使用UITextView只需要提供insets (Objective-C):

textView.textContainerInset = UIEdgeInsetsMake(10, 0, 10, 0);

另外,如果你子类化UILabel,这种方法的一个例子是重写drawTextInRect方法 (objective - c)

- (void)drawTextInRect:(CGRect)uiLabelRect {
    UIEdgeInsets myLabelInsets = {10, 0, 10, 0};
    [super drawTextInRect:UIEdgeInsetsInsetRect(uiLabelRect, myLabelInsets)];
}

您还可以为您的新子类UILabel提供带有insets变量的TOP, LEFT, BOTTOM和RIGHT。

一个示例代码可以是:

In .h (Objective-C)

float topInset, leftInset,bottomInset, rightInset;

在。m (Objective-C)中

- (void)drawTextInRect:(CGRect)uiLabelRect {
    [super drawTextInRect:UIEdgeInsetsInsetRect(uiLabelRect, UIEdgeInsetsMake(topInset,leftInset,bottomInset,rightInset))];
}

从我所看到的,似乎你必须在子类化UILabel时重写它的intrinsicContentSize。

所以你应该像这样重写intrinsicContentSize:

- (CGSize) intrinsicContentSize {
    CGSize intrinsicSuperViewContentSize = [super intrinsicContentSize] ;
    intrinsicSuperViewContentSize.height += topInset + bottomInset ;
    intrinsicSuperViewContentSize.width += leftInset + rightInset ;
    return intrinsicSuperViewContentSize ;
}

并添加以下方法来编辑您的嵌入,而不是单独编辑它们:

- (void) setContentEdgeInsets:(UIEdgeInsets)edgeInsets {
    topInset = edgeInsets.top;
    leftInset = edgeInsets.left;
    rightInset = edgeInsets.right;
    bottomInset = edgeInsets.bottom;
    [self invalidateIntrinsicContentSize] ;
}

它将更新UILabel的大小,以匹配边缘插入并覆盖您提到的多行需求。

经过搜索,我找到了一个IPInsetLabel的Gist。如果这些方法都不起作用,你可以尝试一下。

关于这件事,还有一个类似的问题(重复)。 有关可用解决方案的完整列表,请参阅以下答案:UILabel文本边距

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

填充一个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或任何其他强制调用。正确的解决方案应该在正常的自动布局绘制周期中正常工作。

一个实用的解决方案是添加与主标签高度和颜色相同的空白标签。将主标签的前导/尾距设置为零,对齐垂直中心,并设置所需的宽度。

只需要使用UIButton,它已经内置了。关闭所有额外的按钮功能,你有一个标签,你可以设置边缘instets。

let button = UIButton()
button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
button.setTitle("title", for: .normal)
button.tintColor = .white // this will be the textColor
button.isUserInteractionEnabled = false