简体   繁体   English

调整 UILabel 的大小以适应插图

[英]Resizing a UILabel to accommodate insets

I'm building a screen to scan barcodes, and I need to put a translucent screen behind some UILabels to improve visibility against light backgrounds.我正在构建一个屏幕来扫描条形码,我需要在一些UILabels后面放置一个半透明屏幕以提高对浅色背景的可见性。

Here's what the screen looks like now:下面是屏幕现在的样子:

在此处输入图片说明

I'm setting the background color on the UILabel to get the translucent boxes.我在UILabel上设置背景颜色以获取半透明框。 I've also created a custom UILabel subclass to allow me to set some padding between the edge of the UILabel and the text usingthis approach .我还创建了一个自定义UILabel子类,以允许我使用这种方法UILabel的边缘和文本之间设置一些填充。

As you can see in the screen above, the UILabel doesn't resize correctly to take the padding into account.正如您在上面的屏幕中看到的, UILabel没有正确调整大小以考虑填充。 The "padding" just shifts the text over without changing the width of the label, causing the text to truncate. “填充”只是将文本移过而不改变标签的宽度,导致文本被截断。

Both of these labels will contain text of arbitrary lengths, and I really need the UILabel to dynamically resize.这两个标签都将包含任意长度的文本,我真的需要UILabel来动态调整大小。

What UILabel method can I override to increase the width of the label and factor in the padding?我可以重写什么UILabel方法来增加标签的宽度和填充因子?

Here's a label class that calculates sizes correctly.这是一个可以正确计算尺寸的标签类。 The posted code is in Swift 3, but you can also download Swift 2 or Objective-C versions. 发布的代码使用 Swift 3,但您也可以下载Swift 2Objective-C版本。

How does it work?它是如何工作的?

By calculating the proper textRect all of the sizeToFit and auto layout stuff works as expected.通过计算正确的 textRect,所有的sizeToFit和自动布局都按预期工作。 The trick is to first subtract the insets, then calculate the original label bounds, and finally to add the insets again.诀窍是先减去插图,然后计算原始标签边界,最后再次添加插图。

Code代码

class NRLabel : UILabel {

    var textInsets = UIEdgeInsets.zero {
        didSet { invalidateIntrinsicContentSize() }
    }

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

    override func drawText(in rect: CGRect) {
        super.drawText(in: UIEdgeInsetsInsetRect(rect, textInsets))
    }
}

Optional: Interface Builder support可选:接口生成器支持

If you want to setup text insets in storyboards you can use the following extension to enable Interface Builder support:如果您想在故事板中设置文本插入,您可以使用以下扩展来启用 Interface Builder 支持:

@IBDesignable
extension NRLabel {

    // currently UIEdgeInsets is no supported IBDesignable type,
    // so we have to fan it out here:
    @IBInspectable
    var leftTextInset: CGFloat {
        set { textInsets.left = newValue }
        get { return textInsets.left }
    }

    // Same for the right, top and bottom edges.
}

Now you can conveniently setup your insets in IB and then just press ⌘= to adjust the label's size to fit.现在您可以方便地在 IB 中设置您的插图,然后只需按 ⌘= 调整标签的大小以适应。

Disclaimer:免责声明:

All code is in the public domain.所有代码都在公共领域。 Do as you please.随心所欲。

Here is a Swift version of a UILabel subclass (same as @Nikolai's answer) that creates an additional padding around the text of a UILabel:这是 UILabel 子类的 Swift 版本(与@Nikolai 的答案相同),它在 UILabel 的文本周围创建了一个额外的填充:

class EdgeInsetLabel : UILabel {
    var edgeInsets:UIEdgeInsets = UIEdgeInsetsZero

    override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
        var rect = super.textRectForBounds(UIEdgeInsetsInsetRect(bounds, edgeInsets), limitedToNumberOfLines: numberOfLines)

        rect.origin.x -= edgeInsets.left
        rect.origin.y -= edgeInsets.top
        rect.size.width  += (edgeInsets.left + edgeInsets.right);
        rect.size.height += (edgeInsets.top + edgeInsets.bottom);

        return rect
    }

    override func drawTextInRect(rect: CGRect) {
        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, edgeInsets))
    }
}

Here is the C# version (usefull for Xamarin) based on Nikolai's code :这是基于 Nikolai 代码的 C# 版本(对 Xamarin 有用):

public class UIEdgeableLabel : UILabel
{
    public UIEdgeableLabel() : base() { }
    public UIEdgeableLabel(NSCoder coder) : base(coder) { }
    public UIEdgeableLabel(CGRect frame) : base(frame) { }
    protected UIEdgeableLabel(NSObjectFlag t) : base(t) { }

    private UIEdgeInsets _edgeInset = UIEdgeInsets.Zero;
    public UIEdgeInsets EdgeInsets
    {
        get { return _edgeInset; }
        set
        {
            _edgeInset = value;
            this.InvalidateIntrinsicContentSize();
        }
    }

    public override CGRect TextRectForBounds(CGRect bounds, nint numberOfLines)
    {
        var rect = base.TextRectForBounds(EdgeInsets.InsetRect(bounds), numberOfLines);
        return new CGRect(x: rect.X - EdgeInsets.Left,
                          y: rect.Y - EdgeInsets.Top,
                          width: rect.Width + EdgeInsets.Left + EdgeInsets.Right,
                          height: rect.Height + EdgeInsets.Top + EdgeInsets.Bottom);
    }

    public override void DrawText(CGRect rect)
    {
        base.DrawText(this.EdgeInsets.InsetRect(rect));
    }
}

Swift 5 version of Nikolai Ruhe answer: Swift 5 版尼古拉儒赫回答:

extension UIEdgeInsets {
   func apply(_ rect: CGRect) -> CGRect {
      return rect.inset(by: self)
   }
}

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

  override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
    let insetRect = bounds.inset(by: textInsets)
    let textRect = super.textRect(forBounds: insetRect, 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))
  }}

In additions to Nikolai Ruhe 's answer, you need to invalidate intrinsic content size for autolayout to properly recalculate the size changes.除了Nikolai Ruhe的回答之外,您还需要使自动布局的内在内容大小无效,以正确重新计算大小更改。 You would notice this issue if you change edgeInsets over the application lifecycle:如果您在应用程序生命周期内更改 edgeInsets,您会注意到这个问题:

class NRLabel: UILabel {

    var edgeInsets = UIEdgeInsetsZero {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }

    ...
}

Here is an example of what I used for a simple 10 unit padding on the left and right of the label with rounded corners.这是我在带有圆角的标签左侧和右侧使用简单的 10 个单位填充的示例。 Just set the label text to center it's self and make it's class IndentedLabel and the rest takes care of itself.只需将标签文本设置为其自身居中并使其成为 IndentedLabel 类,其余部分自行处理。 To modify the padding just scale up or down rect.size.width += (x)要修改填充只需按比例放大或缩小 rect.size.width += (x)

class IndentedLabel: UILabel {

    var edgeInsets:UIEdgeInsets = UIEdgeInsetsZero

    override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
        var rect = super.textRectForBounds(UIEdgeInsetsInsetRect(bounds, edgeInsets), limitedToNumberOfLines: numberOfLines)

        rect.size.width  += 20;

        return rect
    }

    override func drawTextInRect(rect: CGRect) {
        self.clipsToBounds = true
        self.layer.cornerRadius = 3
        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, edgeInsets))
    }
}

Here's a quick, hacky way to do it that you can understand more quickly.这是一种快速、hacky 的方法,您可以更快地理解它。 It's not as robust as Nikolai's, but it gets the job done.它不如 Nikolai 强大,但可以完成工作。 I did this when I was trying to fit my text in my UILabel within a UITableViewCell:当我尝试将文本放入 UITableViewCell 中的 UILabel 中时,我这样做了:

  1. Set a width constraint for the UILabel为 UILabel 设置宽度约束
  2. Connect the constraint via IBOutlet onto your code, either VC (custom cell class if you're doing an expanding table view cell)通过 IBOutlet 将约束连接到您的代码上,或者是 VC(如果您正在扩展表格视图单元,则为自定义单元类)
  3. Create a variable for the actual size of the text, then add the insets + the width size to the constraint and update the view:为文本的实际大小创建一个变量,然后将 insets + 宽度大小添加到约束并更新视图:

let messageTextSize: CGSize = (messageText as NSString).sizeWithAttributes([ NSFontAttributeName: UIFont.systemFontOfSize(14.0)]) cell.widthConstraint.constant = messageTextSize.width + myInsetsOrWhatever

I haven't extensively tested it yet, you might have to play around with the exact CGFloat values that you add.我还没有对它进行广泛的测试,您可能需要使用您添加的确切 CGFloat 值。 I found that the right size isn't exactly width plus insets;我发现正确的尺寸不完全是宽度加上插图; it's a little larger than that.它比那大一点。 This makes sure that the width of the UILabel will always be at least the text size or larger.这可确保 UILabel 的宽度始终至少为文本大小或更大。

Swift 5 .斯威夫特 5 . You can create a custom UILabel class.您可以创建自定义UILabel类。
I've added 22 paddings to the left side of the content.我在内容的左侧添加了 22 个填充。 When UILabel asks for intrinsicContentSize return by adding padding size you have added, I've added 22 and returned customized size.UILabel通过添加您添加的填充大小要求返回intrinsicContentSize ,我添加了 22 并返回了自定义大小。 That's it.就是这样。

// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation. 
override func draw(_ rect: CGRect) {
        // Drawing code
        let insets = UIEdgeInsets(top: 0, left: 22, bottom: 0, right: 0)
        super.drawText(in: rect.inset(by: insets))
        self.layoutSubviews()
}

// This will return custom size with flexible content size. Mainly it can be used in Chat.
override var intrinsicContentSize: CGSize {
        var size = super.intrinsicContentSize
        size.width = 22 + size.width
        return size
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM