Додавання місця / прокладки до UILabel


187

У мене є місце, UILabelде я хочу додати місця вгорі і внизу. З мінімальною висотою в обмеженні я змінив його на:

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

EDIT: Для цього я використав:

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

    } 

Але я мушу знайти інший метод, тому що якщо я напишу більше двох рядків, проблема однакова:

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


1
Можливий дублікат текстової межі
UILabel

Ми нарешті з’ясували, як саме це зробити у всіх динамічних випадках як ідеальна заміна UILabel, що не потребує перепланування або будь-яких інших проблем. PHEW. stackoverflow.com/a/58876988/294884
Fattie

Відповіді:


122

Якщо ви хочете дотримуватися UILabel, не підкласифікуючи його, Mundi дав вам чітке рішення.

Якщо ж ви хочете уникати обгортання UILabel UIView, ви можете використовувати UITextView, щоб дозволити використання UIEdgeInsets (padding) або підкласу 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.

Прикладним кодом може бути:

In .h (OBJ-C)

float topInset, leftInset,bottomInset, rightInset;

In .m (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

Після трохи пошуків я знайшов цю історію з IPInsetLabel. Якщо жодне з цих рішень не працює, ви можете спробувати.

Правка №3

У цьому питанні було подібне запитання (дублікат).
Повний список доступних рішень див. У відповідь: Поле тексту UILabel


Вибачте, але я вже використовував: `override func drawTextInRect (rect: CGRect) {var insets: UIEdgeInsets = UIEdgeInsets (верх: 0,0, ліворуч: 10,0, знизу: 0,0, праворуч: 10,0) super.drawTextInRect (UIEdgeInsetsInsetRect (rect, inse inse ))} `це не працює, тому що результат однаковий, не працюйте динамічно ..
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 не змінюється, внаслідок чого текст стає "..."
необі

1
@Tai Le, дякую, що поділився, я використав це в перегляді таблиці, я не знаю, чому його обрізання тексту, наприклад. студент стає студентом,
vishwa.deepak

1
@Tim, можливо, ти мав намір скористатисяmin
ielyamani

4
Слова попередження тут. Я використовував це рішення в підкласі UILabel. Під час використання цих міток у багаторядковому режимі, у вертикальному UIStackView виникає проблема. Іноді мітка, здається, загортає текст без правильного розміру мітки - тому слово або 2 закінчується відсутнім у кінці рядка. Зараз у мене немає рішення. Я напишу його тут, якщо зроблю. Я витрачав години, роздумуючи над цим питанням, перш ніж довести, що він тут.
Даррен Блек

1
Для того, щоб це працювало в таких ситуаціях, вам потрібно змінити "setBounds" і встановити self.preferredMaxLayoutWidth на ширину меж, мінус ліві та праві вставки
Arnoud Barisain-

82

Швидкий 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
Це не завжди коректно працює з багаторядковими мітками, оскільки коли мітка обчислює її висоту, вона передбачає нульове набивання.
fhucho

76

Ви можете зробити це правильно від IB:

  1. змінити текст на атрибутивний

приписуваний текст

  1. перейти до випадаючого списку з "..."

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

  1. ви побачите деякі властивості прокладки для рядків, абзаців та тексту відступу першого рядка чи будь-чого, що вам потрібно

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

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

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


1
У моїй розгортці я бачу зміни тексту, але коли я запускаю програму. У тексті не відображаються зміни ... T_T .. моя мітка знаходиться у спеціальній комірці, чи є проблеми?
А. Трехо

1
@ A.Trejo Можливо, ваша власна клітинка встановить властивість мітки під час виконання.
П’єр-Ів Гільємет

1
Зміни можуть з’являтися на табло, але при запуску програми змін не відбувається.
Rhenz

4
Це не застосовується, коли ви встановлюєте текст програмно.
Nij

1
Це не відповідь. У вас є лише контроль над відступом першого рядка, а не прокладка у всіх напрямках.
rommex

49

SWIFT 4

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

Приклад:

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

Розширення UILabel

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 на номерOfLines
Антуан Бодарт

Це чудово, але у мене проблеми з декількома рядками, навіть думав, що я додаю числа рядків 2 або залишаю на 0, він завжди показує один. ви знаєте, як це вирішити?
Маго Ніколя Паласіос

1
@AntoineBodart вам вдалося вирішити проблему numberOfLines?
Маго Ніколя Паласіос

@ AntoineBodart, @Mago Nicolas Palacios - вирішено!
iEvgen Podkorytov

47

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


1
drawTextInRect працює лише на 1 рядок, intrinsicContentSizeне працює з горизонтальною прокладкою. Загорнути UILabel всередину UIView - це хороший шлях
onmyway133

8
Якщо ви знаходитесь в IB, тепер настав час згадати меню Редактор -> Вставити -> Переглянути. Виберіть спочатку свій 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
Хороший підказник, з великою перевагою, що це також можна зробити у програмі Interface Builder
Елі

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)"
Camilo Ortegón

16

Набивання UILabel, повне рішення. Оновлено до 2020 року.

Виявляється, три речі треба зробити.

1. Необхідно зателефонувати textRect # forBounds з новим меншим розміром

2. Необхідно переосмислити малюнокText новим меншим розміром

3. Якщо комірка з динамічним розміром, повинна налаштувати intrinsicContentSize

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

Таким чином, ми беремо "існуючий" intrinsicContentSize і фактично додаємо 80 до висоти .

Повторювати ...

Ви повинні буквально "отримати" висоту, обчислену "поки що" двигуном, і змінити це значення.

Я вважаю цей процес заплутаним, але саме так він працює. Для мене Apple повинна викласти дзвінок під назвою "на кшталт" попередній розрахунок висоти ".

По-друге, ми повинні фактично використовувати виклик textRect # forBounds з нашим новим меншим розміром .

Отже, у textRect # forBounds ми спочатку робимо розмір меншим, а потім називаємо супер.

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

Якщо ви ретельно досліджуєте всі спроби та обговорення на цій сторінці, це саме проблема. Помітьте, що деякі рішення "здаються майже працюючими", але тоді хтось повідомить, що в певних ситуаціях це не вийде. Це дійсно точна причина - заплутано потрібно "зателефонувати супер після цього", а не раніше.

Отже, якщо ви називаєте супер це "в неправильному порядку", воно зазвичай працює, але не для певної довжини тексту .

Ось точний наочний приклад "неправильно робити супер першим":

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

Зауважте, правильність 60,20,20,24, але обчислення розміру насправді невірно, оскільки це було зроблено за схемою "супер перший" у textRect # forBounds.

Виправлено:

Зауважте, що лише зараз движок textRect # forBounds знає, як правильно зробити обчислення :

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

Нарешті!

Знову ж таки, у цьому прикладі UILabel використовується в типовій ситуації, коли фіксується ширина. Отже, в intrinsicContentSize ми повинні "додати" загальну додаткову висоту, яку ми хочемо. (Вам не потрібно жодним чином "додавати" ширину, це було б безглуздо, як це визначено.)

Потім у textRect # forBounds ви отримуєте межі, "запропоновані поки що", шляхом авторозмітки, ви віднімаєте свої поля, і лише потім знову зателефонуєте до двигуна textRect # forBounds, тобто в супер, що дасть результат.

Нарешті і просто в малюнку. Ви, звичайно, малюєте в тому ж меншому полі.

Фу!

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
}

Знову. Зауважте, що відповіді на це та інші питання якості, які є "майже" правильними, зазнають проблеми на першому зображенні вище - "супер не в тому місці" . Ви повинні змусити розмір більше в intrinsicContentSize, а потім у textRect # forBounds спочатку потрібно скоротити межі першої пропозиції, а потім викликати супер.

Підсумок: ви повинні "зателефонувати супер останньою " у textRect # forBounds

У цьому секрет.

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


Порада:

Якщо ви працюєте з шрифтами Monospace, ось чудова порада: https://stackoverflow.com/a/59813420/294884


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)

ЗАЧЕКАЙТЕ - насправді існує проблема, яку я виявив із цим у деяких випадках. Раніше це була найправильніша відповідь. Я вказав правильну відповідь нижче.
Fattie

Я включив зображення у свою відповідь, що показує проблему
Fattie

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 в об’єкт етикетки розкадрівки. Щасливе кодування!


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). Ідентифікатор, як змінити його розмір?
Bogy

1
Я оновив sizeToFit (), щоб він працював із видом для багаторазового використання
Bogy,

sizeToFit()має бути загальнодоступним, оскільки: "Переважаючий метод екземпляра повинен бути таким же доступним, як і його тип", що
додається

7

Я трохи відредагував прийняту відповідь. Виникає проблема, коли leftInsetі rightInsetзбільшиться, частина тексту зникне, б / с ширина мітки звузиться, але висота не збільшується як цифра:

етикетка з підкладкою з неправильним внутрішнім розміром вмісту

Щоб вирішити цю проблему, потрібно перерахувати висоту тексту наступним чином:

@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 Насолоджуйтесь!
Док

на жаль, я не змусив його працювати. Я спробував з нашим кодом швидким 5 override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }
Nazmul Hasan

7

Просто використовуйте автоматичний розклад:

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)-10 та -4 для централізації
Mofe Ejegi

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

6

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

}

4

Підклас UILabel. (File-New-File- CocoaTouchClass-підклас 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 на аркуші розкадрування як клас мітки.


я б голосував, якщо ви зміните ці твердо кодовані значення на властивості класу, я вже використовую цей код.
Хуан Боеро

@ Juan: drawTextInRect - це властивість класу за замовчуванням UILabel, яку ми не можемо змінити за допомогою коду. Найкраща практика підкласи UILabel та додавання необхідних змін кадру. Так чи інакше, це зручно як функція спадкування.
AG

це правильно, проте, як мінімум, для Swift 3 принаймні, intrinsicContentSize - це не функція, а властивість, тому слід "замінити var intrinsicContentSize: CGFloat {}", а не "переосмислити функцію intrinsicContentSize", лише примітку.
joey

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

2

Легке набивання (Swift 3.0, відповідь Елвіна Джорджа):

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

  }

2

Опрацювання відповіді Мунді.

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

схожий на прокладений 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) Додайте обмеження між краями етикетки та панеллю. Це змушує панель триматися на відстані від етикетки. тобто "підкладка"

Редакція: робити все це вручну є надто виснажливим, багатослівним та схильним до помилок. Я пропоную вам вибрати обгортку з автоматичного макета з 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, як у прикладі зображення.

Примітка: потрібно лише додавати позиційні обмеження, а не розмірні обмеження: автоматична компонування вирішить макет на основі як 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)
    }
}

Дякую за публікацію, я шукаю рішення щодо прокладки + обрізки. Мені здається, ваше рішення порушує 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))
    }
}

1

Одне прагматичне рішення - додати порожні мітки такої ж висоти та кольору, що й основна мітка. Встановіть провідний / кінцевий простір для основної мітки до нуля, вирівняйте вертикальні центри та зробіть по ширині потрібний запас.


1

Якщо ви хочете додати 2px прокладки навколо textRect, просто зробіть це:

let insets = UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2)
label.frame = UIEdgeInsetsInsetRect(textRect, insets)

що таке textRect? label.frame?
Gustavo Baiocchi Costa

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

Ціль-С

На основі відповіді Tai Le , яка реалізує функцію всередині 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 безпосередньо в програмі Interface Builder після введення класу всередині XIB або аркуша розкадрів, значення за замовчуванням для вставки - нульове.


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

    }

Версія 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])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.