ios 向 UILabel 添加空间/填充

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27459746/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 04:01:00  来源:igfitidea点击:

Adding space/padding to a UILabel

iosuilabelpadding

提问by Annachiara

I have a UILabelwhere I want to add space in the top and in the bottom. With minimun height in constrainst I've modified it to:

我有一个UILabel我想在顶部和底部添加空间的地方。在约束的最小高度下,我将其修改为:

enter image description here

在此处输入图片说明

EDIT: To do this I've used:

编辑:为此我使用了:

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

    } 

But I've to find a different method because if I write more than two lines the problem is the same:

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

enter image description here

在此处输入图片说明

采纳答案by nunofmendes

If you want to stick with UILabel, without subclassing it, Mundi has given you a clear solution.

如果您想坚持使用 UILabel,而不对其进行子类化,Mundi 为您提供了明确的解决方案。

If alternatively, you would be willing to avoid wrapping the UILabel with a UIView, you could use UITextView to enable the use of UIEdgeInsets (padding) or subclass UILabel to support UIEdgeInsets.

如果或者,您愿意避免使用 UIView 包装 UILabel,则可以使用 UITextView 来启用 UIEdgeInsets(填充)或子类 UILabel 以支持 UIEdgeInsets。

Using a UITextViewwould only need to provide the insets (OBJ-C):

使用UITextView只需要提供插图(OBJ-C):

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

Alternative, if you subclass UILabel, an example to this approach would be overriding the drawTextInRectmethod
(OBJ-C)

或者,如果您继承 UILabel,则此方法的示例将覆盖drawTextInRect方法
(OBJ-C)

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

You could additionally provide your new subclassed UILabel with insets variables for TOP, LEFT, BOTTOM and RIGHT.

您还可以为新的子类 UILabel 提供 TOP、LEFT、BOTTOM 和 RIGHT 的插入变量。

An example code could be:

示例代码可能是:

In .h (OBJ-C)

.h (OBJ-C)

float topInset, leftInset,bottomInset, rightInset;

In .m (OBJ-C)

.m (OBJ-C)

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

EDIT #1:

编辑#1:

From what I have seen, it seems you have to override the intrinsicContentSize of the UILabel when subclassing it.

从我所看到的,似乎你必须在子类化 UILabel 时覆盖其内在内容大小。

So you should override intrinsicContentSizelike:

所以你应该像这样覆盖internalContentSize

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

And add the following method to edit your insets, instead of editing them individually:

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

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

It will update the size of your UILabel to match the edge insets and cover the multiline necessity you referred to.

它将更新 UILabel 的大小以匹配边缘插入并涵盖您提到的多行必要性。

Edit #2

编辑 #2

After searching a bit I have found this Gistwith an IPInsetLabel. If none of those solutions work you could try it out.

经过一番搜索,我找到了这个带有 IPInsetLabel 的Gist。如果这些解决方案都不起作用,您可以尝试一下。

Edit #3

编辑 #3

There was a similar question (duplicate) about this matter.
For a full list of available solutions, see this answer: UILabel text margin

关于这个问题有一个类似的问题(重复)。
有关可用解决方案的完整列表,请参阅此答案:UILabel text margin

回答by Tai Le

I have tried with it on Swift 4.2, hopefully it work for you!

我已经在Swift 4.2上尝试过,希望它对你有用!

@IBDesignable class PaddingLabel: 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

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

    override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(width: size.width + leftInset + rightInset,
                      height: size.height + topInset + bottomInset)
    }   

    override var bounds: CGRect {
        didSet {
            // ensures this works within stack views if multi-line
            preferredMaxLayoutWidth = bounds.width - (leftInset + rightInset)
        }
    } 
}

回答by zombie

Swift 3

斯威夫特 3

import UIKit

class PaddingLabel: UILabel {

   @IBInspectable var topInset: CGFloat = 5.0
   @IBInspectable var bottomInset: CGFloat = 5.0
   @IBInspectable var leftInset: CGFloat = 5.0
   @IBInspectable var rightInset: CGFloat = 5.0

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

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

回答by Pierre-Yves Guillemet

You can do it properly from IB :

你可以从 IB 正确地做到这一点:

  1. change the text to attributed
  1. 将文本更改为归因

attributed text

属性文本

  1. go to dropdown list with "..."
  1. 转到带有“...”的下拉列表

enter image description here

在此处输入图片说明

  1. you will see some padding properties for the lines, paragraphs and text change indent first line or anything you want
  1. 您将看到行、段落和文本的一些填充属性更改缩进第一行或您想要的任何内容

enter image description here

在此处输入图片说明

  1. check the result
  1. 检查结果

enter image description here

在此处输入图片说明

回答by iEvgen Podkorytov

SWIFT 4

快速 4

Easy to use solution, available for all UILabel child in project.

易于使用的解决方案,适用于项目中的所有 UILabel 子项。

Example:

例子:

let label = UILabel()
    label.<Do something>
    label.padding = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0)

UILabel Extension

UILabel 扩展

import UIKit

extension UILabel {
    private struct AssociatedKeys {
        static var padding = UIEdgeInsets()
    }

    public var padding: UIEdgeInsets? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.padding) as? UIEdgeInsets
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(self, &AssociatedKeys.padding, newValue as UIEdgeInsets?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
    }

    override open func draw(_ rect: CGRect) {
        if let insets = padding {
            self.drawText(in: rect.inset(by: insets))
        } else {
            self.drawText(in: rect)
        }
    }

    override open var intrinsicContentSize: CGSize {
        guard let text = self.text else { return super.intrinsicContentSize }

        var contentSize = super.intrinsicContentSize
        var textWidth: CGFloat = frame.size.width
        var insetsHeight: CGFloat = 0.0
        var insetsWidth: CGFloat = 0.0

        if let insets = padding {
            insetsWidth += insets.left + insets.right
            insetsHeight += insets.top + insets.bottom
            textWidth -= insetsWidth
        }

        let newSize = text.boundingRect(with: CGSize(width: textWidth, height: CGFloat.greatestFiniteMagnitude),
                                        options: NSStringDrawingOptions.usesLineFragmentOrigin,
                                        attributes: [NSAttributedString.Key.font: self.font], context: nil)

        contentSize.height = ceil(newSize.size.height) + insetsHeight
        contentSize.width = ceil(newSize.size.width) + insetsWidth

        return contentSize
    }
}

回答by Mundi

Just use a UIViewas a superview and define a fixed margin to the label with auto layout.

只需将 aUIView作为超级视图并使用自动布局为标签定义固定边距。

回答by Steve M

Just use a UIButton, its already built in. Turn off all the extra button features and you have a label that you can set edge instets on.

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

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

回答by Alice Chan

Without Storyboard:

没有故事板:

class PaddingLabel: UILabel {

    var topInset: CGFloat
    var bottomInset: CGFloat
    var leftInset: CGFloat
    var rightInset: CGFloat

    required init(withInsets top: CGFloat, _ bottom: CGFloat,_ left: CGFloat,_ right: CGFloat) {
        self.topInset = top
        self.bottomInset = bottom
        self.leftInset = left
        self.rightInset = right
        super.init(frame: CGRect.zero)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

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

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

Usage:

用法:

let label = PaddingLabel(8, 8, 16, 16)
label.font = .boldSystemFont(ofSize: 16)
label.text = "Hello World"
label.backgroundColor = .black
label.textColor = .white
label.textAlignment = .center
label.layer.cornerRadius = 8
label.clipsToBounds = true
label.sizeToFit()

view.addSubview(label)

Result:

结果:

回答by Fattie

Padding a UILabel, full solution. Updated for 2020.

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

It turns out there are three thingsthat must be done.

事实证明,必须做三件事

1. Must call textRect#forBounds with the new smaller size

1. 必须使用新的较小尺寸调用 textRect#forBounds

2. Must override drawText with the new smaller size

2. 必须用新的较小尺寸覆盖 drawText

3. If a dynamically sized cell, must adjust intrinsicContentSize

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

In the typical example below, the text unit is in a table view, stack view or similar construction, which gives it a fixed width. In the example we want padding of 60,20,20,24.

在下面的典型示例中,文本单元位于表格视图、堆栈视图或类似结构中,这使其具有固定的宽度。在示例中,我们想要填充 60、20、20、24。

Thus, we take the "existing" intrinsicContentSize and actually add 80 to the height.

因此,我们采用“现有”的内在内容大小并实际将 80 添加到高度

To repeat ...

重复 ...

You have to literally "get" the height calculated "so far" by the engine, and changethat value.

您必须从字面上“获取”引擎“到目前为止”计算的高度,然后更改该值。

I find that process confusing, but, that is how it works. For me, Apple should expose a call named something like "preliminary height calculation".

我发现这个过程令人困惑,但是,这就是它的工作原理。对我来说,Apple 应该公开一个名为“初步高度计算”之类的调用。

Secondly we have to actually use the textRect#forBounds call with our new smaller size.

其次,我们必须实际使用 textRect#forBounds 调用与我们新的较小 size

So in textRect#forBounds we firstmake the size smaller and thencall super.

所以在 textRect#forBounds 中,我们首先使尺寸变小,然后调用 super。

Alert! You mustcall super after, not before!

警报!你必须在之后调用 super ,而不是之前!

If you carefully investigate all the attempts and discussion on this page, that is the exact problem. Notice some solutions "seem to almost work" but then someone will report that in certain situations it will not work. This is indeed the exact reason - confusingly you must "call super afterwards", not before.

如果您仔细调查此页面上的所有尝试和讨论,那就是确切的问题。注意一些解决方案“似乎几乎可以工作”,但是有人会报告说在某些情况下它不起作用。这确实是确切的原因 - 令人困惑的是,您必须“事后调用 super”,而不是之前。

So, if you call super it "in the wrong order", it usually works, but does not for certain specific text lengths.

因此,如果您将 super 称为“以错误的顺序”,它通常有效,但不适用于某些特定的文本长度

Here is an exact visual example of "incorrectly doing super first":

这是“错误地首先执行超级操作”的精确视觉示例:

enter image description here

在此处输入图片说明

Notice the 60,20,20,24 margins are correct BUT the size calculation is actually wrong, because it was done with the "super first" pattern in textRect#forBounds.

请注意 60,20,20,24 边距是正确的,但大小计算实际上是错误的,因为它是通过 textRect#forBounds 中的“超级优先”模式完成的。

Fixed:

固定的:

Notice that only now does the textRect#forBounds engine know how to do the calculation properly:

请注意,只有现在 textRect#forBounds 引擎才知道如何正确进行计算

enter image description here

在此处输入图片说明

Finally!

最后!

Again, in this example the UILabel is being used in the typical situation where width is fixed. So in intrinsicContentSize we have to "add" the overall extra height we want. (You don't need to "add" in any way to the width, that would be meaningless as it is fixed.)

同样,在此示例中,UILabel 用于宽度固定的典型情况。所以在 internalContentSize 中,我们必须“添加”我们想要的整体额外高度。(您不需要以任何方式“添加”宽度,因为它是固定的,所以没有意义。)

Then in textRect#forBounds you get the bounds "suggested so far" by autolayout, you subtractyour margins, and only thencall again to the textRect#forBounds engine, that is to say in super, which will give you a result.

然后在 textRect#forBounds 中,您会通过自动布局获得“到目前为止建议的”边界,减去您的边距,然后再次调用 textRect#forBounds 引擎,也就是说在超级中,这会给您一个结果。

Finally and simply in drawText you of course draw in that same smaller box.

最后,简单地在 drawText 中,您当然可以在同一个较小的框中进行绘制。

Phew!

呼!

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

Once again. Note that the answers on this and other QA that are "almost" correct suffer the problem in the first image above - the "super is in the wrong place". You must force the size bigger in intrinsicContentSize and then in textRect#forBounds you must firstshrink the first-suggestion bounds and thencall super.

再来一次。请注意,此问题和其他“几乎”正确的 QA 答案遇到了上面第一张图片中的问题—— “超级在错误的地方”。您必须在 internalContentSize 中强制更大的大小,然后在 textRect#forBounds 中,您必须首先缩小第一个建议范围,然后调用 super。

Summary: you must "call super last" in textRect#forBounds

总结:你必须在 textRect#forBounds 中“call super last

That's the secret.

这就是秘密。

Note that you do not need to and should not need to additionally call invalidate, sizeThatFits, needsLayout or any other forcing call. A correct solution should work properly in the normal autolayout draw cycle.

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



Tip:

提示:

If you're working with monospace fonts, here's a great tip: https://stackoverflow.com/a/59813420/294884

如果您使用等宽字体,这里有一个很好的提示:https: //stackoverflow.com/a/59813420/294884

回答by LE24

Swift 4+

斯威夫特 4+

class EdgeInsetLabel: UILabel {
    var textInsets = UIEdgeInsets.zero {
        didSet { invalidateIntrinsicContentSize() }
    }

    override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        let invertedInsets = UIEdgeInsets(top: -textInsets.top,
                                          left: -textInsets.left,
                                          bottom: -textInsets.bottom,
                                          right: -textInsets.right)
        return textRect.inset(by: invertedInsets)
    }

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

usage:

用法:

let label = EdgeInsetLabel()
label.textInsets = UIEdgeInsets(top: 2, left: 6, bottom: 2, right: 6)