Добавление пробела / отступов к UILabel

187

У меня есть место, UILabelгде я хочу добавить место сверху и снизу. С минимальной высотой в ограничении я изменил это:

введите описание изображения здесь

РЕДАКТИРОВАТЬ: Для этого я использовал:

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

    } 

Но я должен найти другой метод, потому что, если я напишу больше двух строк, проблема будет та же:

введите описание изображения здесь

Annachiara
источник
1
Возможный дубликат текстового поля
UILabel
Мы наконец-то наконец-то выяснили, как именно это сделать правильно, во всех динамических случаях, в качестве идеальной замены для UILabel без необходимости перекомпоновки или каких-либо других проблем. Уф. stackoverflow.com/a/58876988/294884
Толстяк

Ответы:

122

Если вы хотите придерживаться UILabel без подклассов, Mundi предоставил вам четкое решение.

Если вы хотите избежать упаковки UILabel с UIView, вы можете использовать UITextView, чтобы разрешить использование UIEdgeInsets (заполнение) или подкласса UILabel для поддержки UIEdgeInsets.

Использование UITextView должно было бы обеспечить только вставки (OBJ-C):

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

Альтернативно, если вы подкласс UILabel , примером этого подхода будет переопределение метода drawTextInRect
(OBJ-C)

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

Вы могли бы дополнительно предоставить вашему новому подклассу UILabel переменные-вставки для TOP, LEFT, BOTTOM и RIGHT.

Пример кода может быть:

В .ч (OBJ-C)

float topInset, leftInset,bottomInset, rightInset;

Дюймы (OBJ-C)

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

РЕДАКТИРОВАНИЕ № 1:

Из того, что я видел, кажется, что вы должны переопределить intrinsicContentSize UILabel при создании его подкласса.

Поэтому вы должны переопределить intrinsicContentSize следующим образом:

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

И добавьте следующий метод для редактирования ваших вкладок, вместо того, чтобы редактировать их по отдельности:

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

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

Редактировать № 2

После небольшого поиска я нашел этот Gist с IPInsetLabel. Если ни одно из этих решений не работает, вы можете попробовать его.

Редактировать № 3

Был похожий вопрос (дубликат) по этому вопросу.
Полный список доступных решений см. В следующем ответе: текстовое поле UILabel

nunofmendes
источник
Извините, но я уже использовал: `переопределить 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) ))} `это не работает, потому что результат тот же, не работает динамически ..
Annachiara
Вы пробовали с UITextView вместо UILabel? Или вам действительно нужно использовать UILabel?
nunofmendes
@Annachiara проверьте редактирование, которое я сделал. Посмотри, работает ли это.
nunofmendes
Хорошо. Сработало ли текстовое представление? Извините, что не пишу в Swift, но я все еще в режиме Obj-C. Моя цель с этим кодом состояла в том, чтобы помочь вам прийти к какому-либо заключению. Надеюсь, что сделал.
nunofmendes
1
Использование TextView и некоторых настроек раскадровки и self.textview.textContainerInset = UIEdgeInsetsMake (0, 10, 10, 10); Это наконец работает! Спасибо !
Анначиара
208

Я попробовал с ним на 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)
        }
    } 
}

Или вы можете использовать CocoaPods здесь https://github.com/levantAJ/PaddingLabel

pod 'PaddingLabel', '1.2'

введите описание изображения здесь

Тай Ле
источник
6
Ширина uilabel не изменяется, в результате чего текст становится "..."
neobie
1
@Tai Le, спасибо, что поделились, я использовал его в табличном представлении, я не знаю, почему это обрезка текста, например. студент становится студентом,
vishwa.deepak
1
@Tim, может быть, вы хотели использоватьmin
ielyamani
4
Слово предупреждения здесь. Я использую это решение в подклассе UILabel. При использовании этих меток в многострочном режиме в вертикальном UIStackView возникает проблема. Иногда кажется, что метка оборачивает текст без правильного определения размера метки - поэтому слово или 2 в конце пропускаются в конце строки. У меня сейчас нет решения. Я напишу это здесь, если я сделаю один. Я часами ковырялся в этом вопросе, прежде чем доказывать, что он был здесь.
Даррен Блэк
1
Чтобы это работало в таких ситуациях, вам нужно переопределить «setBounds» и установить self.preferredMaxLayoutWidth равным ширине границ, минус ваши левые и правые вставки
Арно Барисаин-
82

Swift 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
      }
   }
}
зомби
источник
Небольшой комментарий: установите для этого класса метку в инспекторе идентификаторов (пользовательский класс) и используйте новый атрибут в инспекторе атрибутов с именем «Заполнение меток». также ниже 5 отступов не имеет никакого эффекта
Иман Каземайни
3
Это не всегда правильно работает с многострочными метками, потому что, когда метка вычисляет свою высоту, она предполагает заполнение нулями.
фхучо
76

Вы можете сделать это правильно из IB:

  1. изменить текст на приписанный

приписанный текст

  1. перейти к выпадающему списку с "..."

введите описание изображения здесь

  1. вы увидите некоторые свойства отступа для строк, абзацев и отступа для первой строки текста или что угодно

введите описание изображения здесь

  1. проверить результат

введите описание изображения здесь

Пьер-Ив Гийем
источник
1
В моей раскадровке я вижу изменения текста, но когда я запускаю приложение. Текст не показывает изменения ... T_T .. мой ярлык находится внутри пользовательской ячейки, есть ли проблемы?
А. Трехо
1
@ A.Trejo Может быть, ваша пользовательская ячейка устанавливает свойство label во время выполнения.
Пьер-Ив Гийе
1
Изменения могут появиться на раскадровке, но при запуске приложения изменений нет.
Ренз
4
Это не применимо, когда вы устанавливаете текст программно.
Nij
1
Это не ответ. Вы можете контролировать только отступ первой строки, но не отступы во всех направлениях.
Роммекс
49

SWIFT 4

Простое в использовании решение, доступное для всех детей UILabel в проекте.

Пример:

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

UILabel Extension

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
    }
}
Евгений Подкорытов
источник
1
Пожалуйста, кратко объясните свой ответ, а не только почтовый индекс.
lmiguelvargasf
4
Ваше расширение отменяет значение 0 для numberOfLines
Антуан
Это замечательно, но у меня проблемы с несколькими строками, даже при том, что я добавляю номера строк 2 или оставляю в 0, он всегда показывает одну. ты знаешь как это решить?
Маго Николас Паласиос
1
@ AntoineBodart вам удалось решить проблему с NumberOfLines?
Маго Николас Паласиос
@ AntoineBodart, @Mago Николас Паласиос - было решено!
Евгений Подкорытов
47

Просто используйте UIViewкак суперпредставление и определите фиксированное поле для метки с автоматическим макетом.

Mundi
источник
1
drawTextInRect работает только для 1 строки, intrinsicContentSizeне работает с горизонтальным заполнением. Оберните UILabel в UIView - это хороший путь
onmyway133
8
Если вы в IB, сейчас самое время вспомнить меню Editor -> Embed In -> View. Просто сначала выберите свой UILabel :)
Грэм Перкс
46

Просто используйте 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
Стив М
источник
2
Эй, это отличный совет! Никаких расширений не требуется! :-D
Фелипе Ферри
2
Настройка isUserInteractionEnabled = falseудобна для ее отключения.
mxcl
Отличный совет ... Я бы предпочел сделать это, чем идти на расширение.
Росс
1
Хороший совет, с большим преимуществом, что это также может быть сделано в Интерфейсном Разработчике
Ely
1
Лучшее решение без подклассов и т. Д.
MeGaPk
16

Без раскадровки:

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

Использование:

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)

Результат:

Алиса Чан
источник
Работает, но знаете ли вы, как заставить его принимать несколько строк? Просто измените init с помощью «PaddingLabel (withInsets: 8, 8, 16, 16)»
Камило Ортегон,
16

Дополнение UILabel, полное решение. Обновлено на 2020.

Оказывается, есть три вещи, которые необходимо сделать.

1. Должен вызывать textRect # forBounds с новым меньшим размером

2. Необходимо переопределить drawText с новым меньшим размером

3. Если ячейка динамического размера, необходимо настроить intrinsicContentSize

В приведенном ниже типичном примере текстовая единица находится в виде таблицы, в виде стека или в аналогичной конструкции, что дает ей фиксированную ширину . В примере мы хотим заполнить 60,20,20,24.

Таким образом, мы берем «существующий» intrinsicContentSize и фактически добавляем 80 к высоте .

Повторить ...

Вы должны буквально «получить» высоту, рассчитанную «на данный момент» двигателем, и изменить это значение.

Я нахожу этот процесс запутанным, но именно так он и работает. Для меня Apple должна выставить вызов под названием что-то вроде «предварительного расчета высоты».

Во-вторых, мы должны использовать вызов textRect # forBounds с нашим новым меньшим размером .

Поэтому в textRect # forBounds мы сначала уменьшаем размер, а затем вызываем super.

Предупреждение! Вы должны позвонить супер после , а не раньше!

Если вы тщательно исследуете все попытки и обсуждения на этой странице, это точно проблема. Обратите внимание, что некоторые решения «кажутся почти работающими», но затем кто-то сообщит, что в определенных ситуациях это не сработает. Это действительно точная причина - сбивающий с толку вы должны «позвонить супер потом», а не раньше.

Таким образом, если вы называете super это «в неправильном порядке», это обычно работает, но не для определенных конкретных длин текста .

Вот точный визуальный пример «неправильно сначала делать супер»:

введите описание изображения здесь

Обратите внимание, что поля 60,20,20,24 верны, НО вычисление размера на самом деле неверно, потому что это было сделано с помощью шаблона «super first» в textRect # forBounds.

Исправлена:

Обратите внимание, что только теперь движок textRect # forBounds знает, как правильно выполнять вычисления :

введите описание изображения здесь

В заключение!

Опять же, в этом примере UILabel используется в типичной ситуации, когда ширина фиксирована. Таким образом, в intrinsicContentSize мы должны «добавить» необходимую нам дополнительную высоту. (Вам не нужно каким-либо образом "добавлять" ширину, это было бы бессмысленно, поскольку она исправлена.)

Затем в textRect # forBounds вы получаете «предложенные до сих пор» границы с помощью autolayout, вычитаете свои поля и только потом снова вызываете механизм textRect # forBounds, то есть в super, который даст вам результат.

Наконец, просто в drawText вы, конечно, рисуете в той же самой маленькой коробке.

Уф!

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)
    // that line of code MUST be LAST in this function, NOT first
    return ctr
}

Снова. Обратите внимание, что ответы на этот и другие QA, которые являются «почти» правильными, переносят проблему на первом изображении выше - «супер находится в неправильном месте» . Вы должны принудительно увеличить размер в intrinsicContentSize, а затем в textRect # forBounds вы должны сначала уменьшить границы первого предложения, а затем вызвать super.

Резюме: вы должны "вызвать супер последний " в textRect # forBounds

Это секрет.

Обратите внимание, что вам не нужно и не нужно дополнительно вызывать invalidate, sizeThatFits, needsLayout или любой другой принудительный вызов. Правильное решение должно работать должным образом в обычном цикле рисования с автоматическим размещением.


Наконечник:

Если вы работаете с моноширинными шрифтами, вот отличный совет: https://stackoverflow.com/a/59813420/294884

Fattie
источник
11

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

использование:

let label = EdgeInsetLabel()
label.textInsets = UIEdgeInsets(top: 2, left: 6, bottom: 2, right: 6)
LE24
источник
ПОДОЖДИТЕ - на самом деле есть проблема, с которой я столкнулся в некоторых случаях. Раньше это был самый правильный ответ. Я поставил правильный ответ ниже.
Толстяк
Я включил в свой ответ изображение, показывающее проблему
Толстяк
9

Код Swift 3 с примером реализации

class UIMarginLabel: UILabel {

    var topInset:       CGFloat = 0
    var rightInset:     CGFloat = 0
    var bottomInset:    CGFloat = 0
    var leftInset:      CGFloat = 0

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

class LabelVC: UIViewController {

    //Outlets
    @IBOutlet weak var labelWithMargin: UIMarginLabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        //Label settings.
        labelWithMargin.leftInset = 10
        view.layoutIfNeeded()
    }
}

Не забудьте добавить имя класса UIMarginLabel в объект метки раскадровки. Удачного кодирования!

mriaz0011
источник
8

Согласно Swift 4.2 (Xcode 10 beta 6) «UIEdgeInsetsInsetRect» считается устаревшим. Я также объявил класс публичным, чтобы сделать его более полезным.

public class UIPaddedLabel: 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

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

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

    public override func sizeToFit() {
        super.sizeThatFits(intrinsicContentSize)
    }
}
Стефан де Лука
источник
Это работает хорошо. Но я пытаюсь использовать его внутри CollectionViewCell, и он не меняет свой размер после повторного использования (событие после sizeToFit и layoutIfNeeded). Любой идентификатор, как изменить его размер?
Тележка
1
Я обновил sizeToFit (), чтобы он работал в режиме многократного использования
Bogy
sizeToFit()должно быть общедоступным как: «Метод переопределяющего экземпляра должен быть таким же доступным, как и его включающий тип»
psyFi
7

Я немного отредактировал принятый ответ. Существует проблема, когда leftInsetи rightInsetувеличивается, часть текста исчезнет, ​​b / c ширина метки будет сужена, но высота не увеличивается, как показано на рисунке:

метка заполнения с неправильным внутренним размером содержимого

Для решения этой проблемы вам необходимо пересчитать высоту текста следующим образом:

@IBDesignable class PaddingLabel: UILabel {

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

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

  override func intrinsicContentSize() -> CGSize {
    var intrinsicSuperViewContentSize = super.intrinsicContentSize()

    let textWidth = frame.size.width - (self.leftInset + self.rightInset)
    let newSize = self.text!.boundingRectWithSize(CGSizeMake(textWidth, CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: self.font], context: nil)
    intrinsicSuperViewContentSize.height = ceil(newSize.size.height) + self.topInset + self.bottomInset

    return intrinsicSuperViewContentSize
  }
}

и результат:

набивочная метка с правильным внутренним размером содержимого

Я надеюсь помочь некоторым людям в той же ситуации, что и я.

Куанг Тран
источник
2
Если вы планируете использовать Swift 3.0 , вы должны изменить имена функций, поскольку новый язык Apple полностью нарушает предыдущее определение функции. Итак, override func drawTextInRect(rect: CGRect)становится override func drawText(in rect: CGRect)и override func intrinsicContentSize() -> CGSizeстановится override var intrinsicContentSize : CGSize Наслаждайся!
DoK
к сожалению я не заставил это работать. Я попробовал с нашим кодом swift 5 override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }
Назмул Хасан
7

Просто используйте autolayout:

let paddedWidth = myLabel.intrinsicContentSize.width + 2 * padding
myLabel.widthAnchor.constraint(equalToConstant: paddedWidth).isActive = true

Готово.

Тим Шадель
источник
Вы также можете сделать то же самое с высотой.
ShadeToD
Отлично, спасибо.
Майк Таверн
7

Другой вариант без подклассов будет:

  1. Установить метку text
  2. sizeToFit()
  3. затем немного увеличьте высоту метки для имитации заполнения

    label.text = "someText"
    label.textAlignment = .center    
    label.sizeToFit()  
    label.frame = CGRect( x: label.frame.x, y: label.frame.y,width:  label.frame.width + 20,height: label.frame.height + 8)
парень
источник
Удивительно, но это было все, что мне было нужно, только немного label.frame = CGRect( x: label.frame.origin.x - 10, y: label.frame.origin.y - 4, width: label.frame.width + 20,height: label.frame.height + 8)
изменив
6

Swift 3, решение iOS10:

open class UIInsetLabel: UILabel {

    open var insets : UIEdgeInsets = UIEdgeInsets() {
        didSet {
            super.invalidateIntrinsicContentSize()
        }
    }

    open override var intrinsicContentSize: CGSize {
        var size = super.intrinsicContentSize
        size.width += insets.left + insets.right
        size.height += insets.top + insets.bottom
        return size
    }

    override open func drawText(in rect: CGRect) {
        return super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }
}
andrewz
источник
6

В Свифт 3

лучший и простой способ

class UILabelPadded: UILabel {
     override func drawText(in rect: CGRect) {
     let insets = UIEdgeInsets.init(top: 0, left: 5, bottom: 0, right: 5)
     super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

}
SanRam
источник
4

Подкласс UILabel. (File-New-File- CocoaTouchClass-make Подкласс UILabel).

//  sampleLabel.swift

import UIKit

class sampleLabel: UILabel {

 let topInset = CGFloat(5.0), bottomInset = CGFloat(5.0), leftInset = CGFloat(8.0), rightInset = CGFloat(8.0)

 override func drawTextInRect(rect: CGRect) {

  let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
  super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))

 }
 override func intrinsicContentSize() -> CGSize {
  var intrinsicSuperViewContentSize = super.intrinsicContentSize()
  intrinsicSuperViewContentSize.height += topInset + bottomInset
  intrinsicSuperViewContentSize.width += leftInset + rightInset
  return intrinsicSuperViewContentSize
 }
}

На ViewController:

override func viewDidLoad() {
  super.viewDidLoad()

  let labelName = sampleLabel(frame: CGRectMake(0, 100, 300, 25))
  labelName.text = "Sample Label"
  labelName.backgroundColor =  UIColor.grayColor()

  labelName.textColor = UIColor.redColor()
  labelName.shadowColor = UIColor.blackColor()
  labelName.font = UIFont(name: "HelveticaNeue", size: CGFloat(22))
  self.view.addSubview(labelName)
 }

ИЛИ Свяжите пользовательский класс UILabel на раскадровке как класс Label.

AG
источник
Я бы проголосовал, если вы измените эти жестко закодированные значения в свойства класса, я уже использую этот код.
Хуан Боэро
@ Juan: drawTextInRect - это свойство класса по умолчанию UILabel, которое мы не можем переопределить с помощью кода. Лучшая практика для создания подкласса UILabel и добавления требуемой смены кадров. Во всяком случае, это удобно в качестве функции наследования.
AG
это правильно, однако, по крайней мере, начиная с Swift 3, intrinsicContentSize является не функцией, а скорее свойством, поэтому должно быть «override var intrinsicContentSize: CGFloat {}» вместо «override func intrinsicContentSize», просто примечание.
Джои
4

Как и другие ответы, но исправьте ошибку. Когда label.widthуправляется автоматическим макетом, иногда текст будет обрезан.

@IBDesignable
class InsetLabel: UILabel {

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

    var insets: UIEdgeInsets {
        get {
            return UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        }
        set {
            topInset = newValue.top
            leftInset = newValue.left
            bottomInset = newValue.bottom
            rightInset = newValue.right
        }
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var adjSize = super.sizeThatFits(size)
        adjSize.width += leftInset + rightInset
        adjSize.height += topInset + bottomInset
        return adjSize
    }

    override var intrinsicContentSize: CGSize {
        let systemContentSize = super.intrinsicContentSize
        let adjustSize = CGSize(width: systemContentSize.width + leftInset + rightInset, height: systemContentSize.height + topInset +  bottomInset) 
        if adjustSize.width > preferredMaxLayoutWidth && preferredMaxLayoutWidth != 0 {
            let constraintSize = CGSize(width: bounds.width - (leftInset + rightInset), height: .greatestFiniteMagnitude)
            let newSize = super.sizeThatFits(constraintSize)
            return CGSize(width: systemContentSize.width, height: ceil(newSize.height) + topInset + bottomInset)
        } else {
            return adjustSize
        }
    }

    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: insets))
    }
}
PowHu
источник
2

Легкая прокладка (Swift 3.0, Alvin George answer):

  class NewLabel: UILabel {

        override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
                return self.bounds.insetBy(dx: CGFloat(15.0), dy: CGFloat(15.0))
        }

        override func draw(_ rect: CGRect) {
                super.drawText(in: self.bounds.insetBy(dx: CGFloat(5.0), dy: CGFloat(5.0)))
        }

  }
odemolliens
источник
2

Уточнение ответа Мунди.

т. е. встраивание метки в UIViewи применение заполнения через Auto Layout. Пример:

выглядит как мягкий UILabel

Обзор:

1) Создайте UIView(«панель») и установите его внешний вид.

2) Создайте UILabelи добавьте его на панель.

3) Добавить ограничения для обеспечения заполнения.

4) Добавьте панель в иерархию видов, затем расположите панель.

Подробности:

1) Создайте вид панели.

let panel = UIView()
panel.backgroundColor = .green
panel.layer.cornerRadius = 12

2) Создайте ярлык, добавьте его на панель в качестве подпредставления.

let label = UILabel()
panel.addSubview(label)

3) Добавьте ограничения между краями метки и панели. Это заставляет панель держаться на расстоянии от метки. то есть "набивка"

От редакции: делать все это вручную очень утомительно, многословно и подвержено ошибкам. Я предлагаю вам выбрать оболочку Auto Layout из github или написать ее самостоятельно

label.panel.translatesAutoresizingMaskIntoConstraints = false
label.topAnchor.constraint(equalTo: panel.topAnchor,
    constant: vPadding).isActive = true
label.bottomAnchor.constraint(equalTo: panel.bottomAnchor,
    constant: -vPadding).isActive = true
label.leadingAnchor.constraint(equalTo: panel.leadingAnchor,
    constant: hPadding).isActive = true
label.trailingAnchor.constraint(equalTo: panel.trailingAnchor,
    constant: -hPadding).isActive = true

label.textAlignment = .center

4) Добавьте панель в иерархию видов, а затем добавьте ограничения позиционирования. Например, обнимите правую часть tableViewCell, как в примере изображения.

Примечание: вам нужно только добавить позиционные ограничения, а не размерные ограничения: Auto Layout будет решать макет на основе как intrinsicContentSizeметки, так и ограничений, добавленных ранее.

hostView.addSubview(panel)
panel.translatesAutoresizingMaskIntoConstraints = false
panel.trailingAnchor.constraint(equalTo: hostView.trailingAnchor,
    constant: -16).isActive = true
panel.centerYAnchor.constraint(equalTo: hostView.centerYAnchor).isActive = true
Уомбл
источник
2

Используйте этот код, если вы столкнулись с проблемой обрезки текста при применении отступов.

@IBDesignable 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.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

    override var intrinsicContentSize: CGSize {
        var intrinsicSuperViewContentSize = super.intrinsicContentSize
        let textWidth = frame.size.width - (self.leftInset + self.rightInset)
        let newSize = self.text!.boundingRect(with: CGSize(textWidth, CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: self.font], context: nil)
        intrinsicSuperViewContentSize.height = ceil(newSize.size.height) + self.topInset + self.bottomInset
        return intrinsicSuperViewContentSize
    }
}

extension CGSize{
    init(_ width:CGFloat,_ height:CGFloat) {
        self.init(width:width,height:height)
    }
}
ceekay
источник
Спасибо за размещение, я ищу решение относительно заполнения + обрезки. Мне кажется, ваше решение нарушает label.numberOfLines = 0, которое мне нужно. Любое решение?
Дон
1

Аналогично другим ответам, но с классом func для динамической настройки заполнения:

class UILabelExtendedView: UILabel
{
    var topInset: CGFloat = 4.0
    var bottomInset: CGFloat = 4.0
    var leftInset: CGFloat = 8.0
    var rightInset: CGFloat = 8.0

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

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

    func setPadding(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat){
        self.topInset = top
        self.bottomInset = bottom
        self.leftInset = left
        self.rightInset = right
        let insets: UIEdgeInsets = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
        super.drawText(in: UIEdgeInsetsInsetRect(self.frame, insets))
    }
}
Pelanes
источник
1

Одним из прагматических решений является добавление пустых ярлыков той же высоты и цвета, что и основной ярлык. Установите начальное / конечное пространство главной метки на ноль, выровняйте вертикальные центры и сделайте ширину желаемым полем.

pks1981
источник
1

Если вы хотите добавить 2px отступы вокруг textRect, просто сделайте это:

let insets = UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2)
label.frame = UIEdgeInsetsInsetRect(textRect, insets)
zs2020
источник
что такое textRect? label.frame?
Густаво Байокки Коста
1

Если вы не хотите или вам не нужно использовать @IBInspectable / @IBDesignable UILabel в раскадровке (я думаю, что они все равно отображаются слишком медленно), тогда лучше использовать UIEdgeInsets вместо 4 разных CGFloats.

Пример кода для Swift 4.2:

class UIPaddedLabel: UILabel {
    var padding = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

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

    public override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(width: size.width + padding.left + padding.right,
                      height: size.height + padding.top + padding.bottom)
    }
}
Саймон Бэкс
источник
1

Objective-C

На основании ответа Тай Ле , который реализует функцию внутри IB Designable, вот версия Objective-C.

Поместите это в YourLabel.h

@interface YourLabel : UILabel

@property IBInspectable CGFloat topInset;
@property IBInspectable CGFloat bottomInset;
@property IBInspectable CGFloat leftInset;
@property IBInspectable CGFloat rightInset;

@end

И это будет идти в YourLabel.m

IB_DESIGNABLE

@implementation YourLabel

#pragma mark - Super

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        self.topInset = 0;
        self.bottomInset = 0;
        self.leftInset = 0;
        self.rightInset = 0;
    }
    return self;
}

- (void)drawTextInRect:(CGRect)rect {
    UIEdgeInsets insets = UIEdgeInsetsMake(self.topInset, self.leftInset, self.bottomInset, self.rightInset);
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}

- (CGSize)intrinsicContentSize {

    CGSize size = [super intrinsicContentSize];
    return CGSizeMake(size.width + self.leftInset + self.rightInset,
                      size.height + self.topInset + self.bottomInset);
}

@end

Затем вы можете изменить вставки YourLabel непосредственно в Интерфейсном Разработчике после указания класса внутри XIB или раскадровки, значение по умолчанию для вставок равно нулю.

lmarceau
источник
0

Простой способ

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.view.addSubview(makeLabel("my title",x: 0, y: 100, w: 320, h: 30))
    }

    func makeLabel(title:String, x:CGFloat, y:CGFloat, w:CGFloat, h:CGFloat)->UILabel{
        var myLabel : UILabel = UILabel(frame: CGRectMake(x,y,w,h))
        myLabel.textAlignment = NSTextAlignment.Right

        // inser last char to right
        var titlePlus1char = "\(title)1"
        myLabel.text = titlePlus1char
        var titleSize:Int = count(titlePlus1char)-1

        myLabel.textColor = UIColor(red:1.0, green:1.0,blue:1.0,alpha:1.0)
        myLabel.backgroundColor = UIColor(red: 214/255, green: 167/255, blue: 0/255,alpha:1.0)


        // create myMutable String
        var myMutableString = NSMutableAttributedString()

        // create myMutable font
        myMutableString = NSMutableAttributedString(string: titlePlus1char, attributes: [NSFontAttributeName:UIFont(name: "HelveticaNeue", size: 20)!])

        // set margin size
        myMutableString.addAttribute(NSFontAttributeName, value: UIFont(name: "HelveticaNeue", size: 10)!, range: NSRange(location: titleSize,length: 1))

        // set last char to alpha 0
        myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor(red:1.0, green:1.0,blue:1.0,alpha:0), range: NSRange(location: titleSize,length: 1))

        myLabel.attributedText = myMutableString

        return myLabel
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}
Алекс Фрейтас
источник
0

Легкая прокладка:

import UIKit

    class NewLabel: UILabel {

        override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {

            return CGRectInset(self.bounds, CGFloat(15.0), CGFloat(15.0))
        }

        override func drawRect(rect: CGRect) {

            super.drawTextInRect(CGRectInset(self.bounds,CGFloat(5.0), CGFloat(5.0)))
        }

    }
AG
источник
Версия 3.0 на нижней странице
odemolliens
0

Свифт 4+

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 10

// Swift 4.2++
label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])

// Swift 4.1--
label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedStringKey.paragraphStyle: paragraphStyle])
Максим Гридин
источник