调整UILabel的大小以容纳插入内容

时间:2014-01-16 16:22:12

标签: ios iphone

我正在构建一个扫描条形码的屏幕,我需要在一些UILabels后面放置一个半透明屏幕,以提高对浅色背景的可见度。

以下是屏幕现在的样子:

enter image description here

我在UILabel设置背景颜色以获得半透明框。我还创建了一个自定义UILabel子类,允许我使用this approachUILabel的边缘和文本之间设置一些填充。

正如您在上面的屏幕中看到的那样,UILabel未正确调整大小以将填充考虑在内。 “填充”只是将文本移动而不改变标签的宽度,导致文本被截断。

这两个标签都包含任意长度的文本,我真的需要UILabel来动态调整大小。

我可以覆盖哪种UILabel方法来增加标签的宽度和填充中的因子?

8 个答案:

答案 0 :(得分:180)

这是一个正确计算尺寸的标签类。 posted code位于Swift 3中,但您也可以下载Swift 2Objective-C版本。

它是如何运作的?

通过计算正确的textRect,所有sizeToFit和自动布局内容都按预期工作。诀窍是首先减去插图,然后计算原始标签边界,最后再添加插入。

代码

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

可选:Interface Builder支持

如果要在故事板中设置文本插入,可以使用以下扩展来启用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.
}

现在您可以在IB中方便地设置插图,然后只需按⌘=调整标签的大小即可。

声明:

所有代码都在公共领域。请随便。

答案 1 :(得分:14)

这是一个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))
    }
}

答案 2 :(得分:4)

这是基于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));
    }
}

答案 3 :(得分:2)

除了 Nikolai Ruhe 的回答之外,您还需要使自动布局的内在内容大小无效,以正确重新计算大小更改。如果在应用程序生命周期中更改edgeInsets,您会注意到此问题:

class NRLabel: UILabel {

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

    ...
}

答案 4 :(得分:1)

尼古拉·茹厄(Nikolai Ruhe)的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))
  }}

答案 5 :(得分:0)

这是我用于带有圆角的标签左右两侧的简单10单位填充的示例。只需设置标签文本以使其自我居中并使其成为IndentedLabel类,其余部分将自行处理。要修改填充,只需向上或向下缩放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))
    }
}

答案 6 :(得分:0)

这是一种快速,黑客的方式,你可以更快地理解它。它不像尼古拉那样强大,但它完成了工作。当我试图在UITabel中将我的文本放入UITabel中时,我这样做了:

  1. 为UILabel设置宽度约束
  2. 通过IBOutlet将约束连接到您的代码上,VC(自定义单元格类,如果您正在进行扩展表视图单元格)
  3. 为文本的实际大小创建一个变量,然后将insets + width size添加到约束中并更新视图:
  4. let messageTextSize: CGSize = (messageText as NSString).sizeWithAttributes([ NSFontAttributeName: UIFont.systemFontOfSize(14.0)]) cell.widthConstraint.constant = messageTextSize.width + myInsetsOrWhatever

    我还没有进行过广泛的测试,你可能需要使用你添加的确切CGFloat值。我发现正确的尺寸不是宽度加上插图;它比那个大一点。这样可以确保UILabel的宽度始终至少为文本大小或更大。

答案 7 :(得分:0)

迅速5 。 您可以创建自定义UILabel类。
我在内容的左侧添加了22个填充。当UILabel通过添加您添加的填充尺寸要求intrinsicContentSize返回时,我添加了22并返回了自定义尺寸。就是这样。

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