簡體   English   中英

調整 UILabel 的大小以適應插圖

[英]Resizing a UILabel to accommodate insets

我正在構建一個屏幕來掃描條形碼,我需要在一些UILabels后面放置一個半透明屏幕以提高對淺色背景的可見性。

下面是屏幕現在的樣子:

在此處輸入圖片說明

我在UILabel上設置背景顏色以獲取半透明框。 我還創建了一個自定義UILabel子類,以允許我使用這種方法UILabel的邊緣和文本之間設置一些填充。

正如您在上面的屏幕中看到的, UILabel沒有正確調整大小以考慮填充。 “填充”只是將文本移過而不改變標簽的寬度,導致文本被截斷。

這兩個標簽都將包含任意長度的文本,我真的需要UILabel來動態調整大小。

我可以重寫什么UILabel方法來增加標簽的寬度和填充因子?

這是一個可以正確計算尺寸的標簽類。 發布的代碼使用 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 支持:

@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 中設置您的插圖,然后只需按 ⌘= 調整標簽的大小以適應。

免責聲明:

所有代碼都在公共領域。 隨心所欲。

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

這是基於 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 版尼古拉儒赫回答:

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

除了Nikolai Ruhe的回答之外,您還需要使自動布局的內在內容大小無效,以正確重新計算大小更改。 如果您在應用程序生命周期內更改 edgeInsets,您會注意到這個問題:

class NRLabel: UILabel {

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

    ...
}

這是我在帶有圓角的標簽左側和右側使用簡單的 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))
    }
}

這是一種快速、hacky 的方法,您可以更快地理解它。 它不如 Nikolai 強大,但可以完成工作。 當我嘗試將文本放入 UITableViewCell 中的 UILabel 中時,我這樣做了:

  1. 為 UILabel 設置寬度約束
  2. 通過 IBOutlet 將約束連接到您的代碼上,或者是 VC(如果您正在擴展表格視圖單元,則為自定義單元類)
  3. 為文本的實際大小創建一個變量,然后將 insets + 寬度大小添加到約束並更新視圖:

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

我還沒有對它進行廣泛的測試,您可能需要使用您添加的確切 CGFloat 值。 我發現正確的尺寸不完全是寬度加上插圖; 它比那大一點。 這可確保 UILabel 的寬度始終至少為文本大小或更大。

斯威夫特 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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM