Cómo perder margen/relleno en UITextView
Tengo UITextView
en mi aplicación iOS, que muestra una gran cantidad de texto.
Luego estoy paginando este texto usando el parámetro de margen de desplazamiento del archivo UITextView
.
Mi problema es que el relleno UITextView
confunde mis cálculos, ya que parece ser diferente según el tamaño de fuente y el tipo de letra que uso.
¿Es posible eliminar el relleno que rodea el contenido del UITextView
?
Para iOS 7.0, descubrí que el truco contentInset ya no funciona. Este es el código que utilicé para deshacerme del margen/relleno en iOS 7.
Esto lleva el borde izquierdo del texto al borde izquierdo del contenedor:
textView.textContainer.lineFragmentPadding = 0
Esto hace que la parte superior del texto se alinee con la parte superior del contenedor:
textView.textContainerInset = .zero
Ambas líneas son necesarias para eliminar completamente el margen/relleno.
Actualizado para 2023
Es uno de los errores más tontos de iOS.
La clase proporcionada aquí UITextViewFixed
se usa ampliamente y suele ser la solución más razonable en general .
Aquí está la clase:
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
}
}
¡No olvides desactivar scrollEnabled en el Inspector!
La solución funciona correctamente en el guión gráfico.
La solución funciona correctamente en tiempo de ejecución.
Ya terminaste. En general, eso debería ser todo lo que necesitas en la mayoría de los casos .
Incluso si cambia la altura de la vista de texto sobre la marcha , UITextViewFixed
normalmente hace todo lo que necesita.
(Un ejemplo común de cambiar la altura sobre la marcha es cambiarla a medida que el usuario escribe).
Aquí está el UITextView roto de Apple...
Aquí está UITextViewFixed
:
Tenga en cuenta que, por supuesto, debe...
... ¡desactive scrollEnabled en el Inspector!
(Activar scrollEnabled significa "hacer que esta vista se expanda tanto como sea posible verticalmente expandiendo el margen inferior tanto como sea posible").
Algunas cuestiones adicionales
(1) En casos muy inusuales, cuando las alturas cambian dinámicamente, Apple hace algo extraño: agrega espacio adicional en la parte inferior .
¡No realmente! Esta tendría que ser una de las cosas más exasperantes en iOS.
Si encuentra el problema, aquí tiene una "solución rápida" que normalmente ayuda:
...
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
// this is not ideal, but sometimes this "quick fix"
// will solve the "extra space at the bottom" insanity:
var b = bounds
let h = sizeThatFits(CGSize(
width: bounds.size.width,
height: CGFloat.greatestFiniteMagnitude)
).height
b.size.height = h
bounds = b
...
(2) En casos excepcionales, para solucionar otro error sutil de Apple, debes agregar:
override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
super.setContentOffset(contentOffset, animated: false) // sic
}
(3) Podría decirse que deberíamos agregar:
contentInset = UIEdgeInsets.zero
justo después .lineFragmentPadding = 0
en UITextViewFixed
.
Sin embargo, lo creas o no, ¡eso simplemente no funciona en el iOS actual! (Comprobado en 2023). Es posible que sea necesario agregar esa línea en el futuro.
El hecho de que UITextView
iOS esté roto es una de las cosas más extrañas en toda la informática móvil. ¡Diez años de esta pregunta y aún no se ha solucionado!
Finalmente, aquí hay un consejo algo similar para el campo de texto : establezca la longitud máxima de caracteres de un UITextField en Swift
Consejo completamente aleatorio: cómo agregar el "..." al final
A menudo estás utilizando un UITextView "como un UILabel". Entonces quieres que trunque el texto usando puntos suspensivos, "..."
Si es así, agregue:
textContainer.lineBreakMode = .byTruncatingTail
Consejo útil si desea una altura cero cuando no hay ningún texto
A menudo se utiliza una vista de texto para mostrar solo texto. Entonces, usa las líneas "0" para significar que la vista de texto cambiará automáticamente la altura dependiendo de cuántas líneas de texto.
Genial. Pero si no hay ningún texto , desafortunadamente obtendrás la misma altura que si hubiera una línea de texto . La vista de texto nunca "desaparece".
Si quieres que "desaparezca", simplemente agrega esto
override var intrinsicContentSize: CGSize {
var i = super.intrinsicContentSize
print("for \(text) size will be \(i)")
if text == "" { i.height = 1.0 }
print(" but we changed it to \(i)")
return i
}
(Hice una altura de '1', para que quede claro lo que sucede en esa demostración; '0' está bien).
¿Qué pasa con UILabel?
Cuando solo muestra texto, UILabel tiene muchas ventajas sobre UITextView. UILabel no sufre los problemas descritos en esta página de preguntas y respuestas.
De hecho, la razón por la que todos normalmente "nos rendimos" y simplemente usamos UITextView es que es difícil trabajar con UILabel. En particular, es ridículamente difícil simplemente agregar padding , correctamente, a UILabel.
De hecho, aquí hay una discusión completa sobre cómo "finalmente" agregar correctamente relleno a UILabel: Agregar espacio/relleno a UILabel . En algunos casos, si está haciendo un diseño difícil con celdas de altura dinámica, a veces es mejor hacerlo de la manera más difícil con UILabel.
Esta solución alternativa se escribió en 2009, cuando se lanzó iOS 3.0. Ya no se aplica.
Me encontré exactamente con el mismo problema y al final tuve que terminar usando
nameField.contentInset = UIEdgeInsetsMake(-4, -8, 0, 0);
donde nameField es un UITextView
. La fuente que estaba usando era Helvetica de 16 puntos. Es sólo una solución personalizada para el tamaño de campo particular que estaba dibujando. Esto hace que el desplazamiento izquierdo quede alineado con el lado izquierdo y el desplazamiento superior donde lo quiero para el cuadro en el que está dibujado.
Además, esto sólo parece aplicarse UITextViews
cuando se utiliza la alineación predeterminada, es decir,
nameField.textAlignment = NSTextAlignmentLeft;
Alinee a la derecha, por ejemplo, y UIEdgeInsetsMake
parece no tener ningún impacto en el borde derecho.
Como mínimo, usar la propiedad .contentInset le permite colocar sus campos con las posiciones "correctas" y acomodar las desviaciones sin compensar su UITextViews
.
A partir de algunas de las buenas respuestas ya dadas, aquí hay una solución puramente basada en Storyboard/Interfaz Builder que funciona en iOS 7.0+
Configure los atributos de tiempo de ejecución definidos por el usuario de UITextView para las siguientes claves:
textContainer.lineFragmentPadding
textContainerInset