Изменение размера UILabel для размещения вставок

Я создаю экран для сканирования штрих-кодов, и мне нужно поставить прозрачный экран за некоторым UILabels, чтобы улучшить видимость от светлых фонов.

Вот как выглядит экран:

enter image description here

Я устанавливаю цвет фона на UILabel, чтобы получить полупрозрачные боксы. Я также создал пользовательский подкласс UILabel, чтобы я мог установить некоторое дополнение между краем UILabel и текстом, используя этот подход.

Как вы можете видеть на экране выше, UILabel не имеет правильного размера, чтобы учитывать отступы. "Прокладка" просто сдвигает текст без изменения ширины метки, заставляя текст усекать.

Обе эти метки будут содержать текст произвольной длины, и мне действительно нужно UILabel динамически изменять размер.

Какой метод UILabel можно переопределить для увеличения ширины метки и коэффициента в заполнении?

Ответ 1

Здесь класс ярлыков, который правильно вычисляет размеры. опубликованный код находится в Swift 3, но вы также можете скачать Swift 2 или Objective-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, а затем просто нажмите ⌘ =, чтобы настроить размер ярлыка, чтобы он соответствовал.

Отказ от ответственности:

Весь код находится в открытом доступе. Делай как хочешь.

Ответ 2

Вот Swift-версия подкласса UILabel (то же, что и ответ @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))
    }
}

Ответ 3

Вот версия С# (полезно для 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));
    }
}

Ответ 4

В дополнение к ответу Nikolay Ruhe вам необходимо аннулировать собственный размер содержимого для автозапуска, чтобы правильно пересчитать изменения размера. Вы заметили бы эту проблему, если вы измените edgeInsets над жизненным циклом приложения:

class NRLabel: UILabel {

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

    ...
}

Ответ 5

Вот пример того, что я использовал для простого заполнения 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

Вот быстрый, хакерский способ сделать это, чтобы вы могли понять быстрее. Это не так здорово, как Николай, но он выполняет свою работу. Я сделал это, когда пытался установить свой текст в UILabel в UITableViewCell:

  • Задайте ограничение ширины для UILabel
  • Подключить ограничение через IBOutlet к вашему коду, либо VC (пользовательский класс ячеек, если вы делаете расширяющуюся ячейку представления таблицы)
  • Создайте переменную для фактического размера текста, затем добавьте вложения + размер ширины к ограничению и обновите представление:

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

Я еще не проверил его еще раз, вам, возможно, придется поиграть с точными значениями CGFloat, которые вы добавите. Я обнаружил, что правильный размер не является точно шириной плюс вставки; это немного больше, чем это. Это гарантирует, что ширина UILabel всегда будет по крайней мере размером текста или больше.

Ответ 7

Свифт 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))
  }}