Relleno de un UILabel
solución completa. Actualizado para 2020.
Resulta que hay tres cosas que deben hacerse.
1. Debe llamar a textRect # forBounds con el nuevo tamaño más pequeño
2. Debe anular drawText con el nuevo tamaño más pequeño
3. Si una celda de tamaño dinámico, debe ajustar intrinsicContentSize
En el ejemplo típico a continuación, la unidad de texto está en una vista de tabla, vista de pila o construcción similar, lo que le da un ancho fijo . En el ejemplo queremos un relleno de 60,20,20,24.
Por lo tanto, tomamos el tamaño de contenido intrínseco "existente" y en realidad agregamos 80 a la altura .
Repetir ...
Debe literalmente "obtener" la altura calculada "hasta ahora" por el motor, y cambiar ese valor.
Este proceso me parece confuso, pero así es como funciona. Para mí, Apple debería exponer una llamada llamada algo así como "cálculo preliminar de altura".
En segundo lugar, tenemos que usar la llamada textRect # forBounds con nuestro nuevo tamaño más pequeño .
Entonces, en textRect # forBounds primero hacemos el tamaño más pequeño y luego llamamos super.
¡Alerta! ¡ Debes llamar a Super después , no antes!
Si investiga cuidadosamente todos los intentos y discusiones en esta página, ese es el problema exacto. Observe que algunas soluciones "parecen funcionar casi", pero alguien informará que en ciertas situaciones no funcionará. Esta es de hecho la razón exacta: confusamente debes "llamar a super después", no antes.
Por lo tanto, si llama a super "en el orden incorrecto", generalmente funciona, pero no para ciertas longitudes de texto específicas .
Aquí hay un ejemplo visual exacto de "hacer super incorrectamente primero":
Observe que los márgenes 60,20,20,24 son correctos PERO el cálculo del tamaño es realmente incorrecto, ya que se realizó con el patrón "súper primero" en textRect # forBounds.
Fijo:
Observe que solo ahora el motor textRect # forBounds sabe cómo hacer el cálculo correctamente :
¡Finalmente!
Nuevamente, en este ejemplo, UILabel se está utilizando en la situación típica en la que el ancho es fijo. Entonces, en intrinsicContentSize tenemos que "agregar" la altura extra general que queremos. (No necesita "agregar" de ninguna manera al ancho, eso no tendría sentido ya que está arreglado).
Luego, en textRect # forBounds obtienes los límites "sugeridos hasta ahora" por autolayout, restas tus márgenes, y solo entonces vuelves a llamar al motor textRect # forBounds, es decir en super, que te dará un resultado.
Finalmente y simplemente en drawText, por supuesto, dibuja en el mismo cuadro más pequeño.
¡Uf!
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
}
Una vez más. Tenga en cuenta que las respuestas sobre este y otros controles de calidad que son "casi" correctas sufren el problema en la primera imagen de arriba: el "súper está en el lugar equivocado" . Debe forzar el tamaño más grande en intrinsicContentSize y luego en textRect # forBounds primero debe reducir los límites de la primera sugerencia y luego llamar a super.
Resumen: debe "llamar a super last " en textRect # forBounds
Ese es el secreto
Tenga en cuenta que no es necesario ni adicionalmente llamar a invalidar, sizeThatFits, needsLayout o cualquier otra llamada forzada. Una solución correcta debería funcionar correctamente en el ciclo normal de extracción automática.
Propina:
Si está trabajando con fuentes monoespaciales, este es un gran consejo: https://stackoverflow.com/a/59813420/294884