Después de actualizar Xcode 11.2 la aplicación falla: cómo vencer a _UITextLayoutView
\n\n¿Te pasa esto? Estás trabajando tranquilamente en tu proyecto, todo vuela, las pruebas pasan, los usuarios están contentos. Y de repente — ¡pum! — actualizas Xcode a la versión 11.2, ejecutas la aplicación y falla con un misterioso error _UITextLayoutView. ¿Te suena? Si es así, no estás solo. Este problema literalmente explotó Stack Overflow a finales de 2019 (miles de visitas en una semana).
En este artículo no solo te mostraré cómo corregir este error, sino que también explicaré por qué ocurre, cómo diagnosticarlo y, lo más importante, cómo prevenirlo en el futuro. Lo analizaremos todo con ejemplos prácticos en Swift. ¡Allá vamos!
\n\n¿Qué clase de bicho es _UITextLayoutView?
\n\nEmpecemos con la teoría. _UITextLayoutView es una clase interna (privada) de UIKit que se encarga de renderizar texto en UITextView. Apple no la documenta públicamente, por lo que cuando algo salió mal con esta clase en Xcode 11.2, los desarrolladores quedaron en un callejón sin salida. El error se ve más o menos así:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_UITextLayoutView setFrame:]: unrecognized selector sent to instance 0x...'\n\nLa frase clave es "unrecognized selector sent to instance". Esto significa que estás intentando llamar al método setFrame: en un objeto que no lo soporta. Y como _UITextLayoutView es una clase privada, antes de Xcode 11.2 se comportaba de manera diferente.
¿Quién está en el grupo de riesgo?
\n\nEl problema no se manifiesta en todos, solo bajo ciertas condiciones. Este es el escenario típico:
\n- \n
- Usas
UITextViewcon el cambio automático de tamaño (auto-sizing) activado. \n - Tienes una jerarquía de vistas personalizada donde
UITextViewestá anidado en unUIScrollViewoUITableViewCell. \n - Interactúas activamente con el campo de texto: desplazas, editas, cambias la fuente. \n
Si al menos un punto coincide — felicidades, estás en la zona de riesgo. Pero no te preocupes: hay solución, y es elegante.
\n\nPaso 1: Diagnóstico — encuentra al culpable
\n\nAntes de tratar, asegurémonos de que el problema es realmente _UITextLayoutView. Abre la consola de Xcode (View → Debug Area → Activate Console) y ejecuta la aplicación. Si ves un error similar al que mostré arriba, estás en el camino correcto.
Una señal adicional: el fallo ocurre en el primer intento de mostrar el UITextView o al desplazarte dentro de él. Si la aplicación falla inmediatamente después de cargar, probablemente sea esto.
Paso 2: Solución simple y rápida (para los que tienen prisa)
\n\nLa corrección más popular en Stack Overflow es desactivar el cambio automático de tamaño en UITextView. Así se hace:
// En tu viewDidLoad o método de configuración\ntextView.isScrollEnabled = false\ntextView.translatesAutoresizingMaskIntoConstraints = true\n\n¡Pero atención! Esta solución funciona, pero rompe el autoajuste del texto. Si es crítico para ti que UITextView cambie dinámicamente su altura según el contenido, esta opción no servirá. Vamos a profundizar más.
Paso 3: Solución avanzada — sobrescribir layoutSubviews
\n\nApple lanzó un parche en Xcode 11.2.1, pero si por alguna razón te quedaste en la versión 11.2, o quieres una solución universal, esto es lo que debes hacer.
\n\nCrea una subclase de UITextView y sobrescribe el método layoutSubviews:
import UIKit\n\nclass FixedTextView: UITextView {\n override func layoutSubviews() {\n super.layoutSubviews()\n // Limpiamos la capa de subvistas sobrantes que causa el error\n self.subviews.forEach { subview in\n if subview.isKind(of: NSClassFromString("_UITextLayoutView") ?? UIView.self) {\n