Дополнение 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