Siento que es un paradigma bastante común para mostrar / ocultar UIViews
, con mayor frecuencia UILabels
, dependiendo de la lógica empresarial. Mi pregunta es, ¿cuál es la mejor manera de usar AutoLayout para responder a vistas ocultas como si su marco fuera 0x0? Aquí hay un ejemplo de una lista dinámica de 1-3 características.
En este momento tengo un espacio superior de 10 píxeles desde el botón hasta la última etiqueta, que obviamente no se deslizará hacia arriba cuando la etiqueta esté oculta. A partir de ahora, creé una salida para esta restricción y modifiqué la constante dependiendo de cuántas etiquetas estoy mostrando. Obviamente, esto es un poco extraño ya que estoy usando valores constantes negativos para presionar el botón hacia arriba sobre los marcos ocultos. También es malo porque no se limita a los elementos de diseño reales, solo cálculos estáticos furtivos basados en alturas conocidas / relleno de otros elementos, y obviamente lucha contra lo que fue diseñado para AutoLayout.
Obviamente, podría crear nuevas restricciones dependiendo de mis etiquetas dinámicas, pero eso es una gran cantidad de microgestión y mucha verbosidad para tratar de colapsar algunos espacios en blanco. ¿Hay mejores enfoques? ¿Cambiar el tamaño de cuadro 0,0 y dejar que AutoLayout haga lo suyo sin manipular las restricciones? ¿Eliminar vistas por completo?
Honestamente, sin embargo, solo modificar la constante del contexto de la vista oculta requiere una sola línea de código con un cálculo simple. Recreando nuevas restricciones con constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
parece tan pesado.
Editar febrero de 2018 : ver la respuesta de Ben con UIStackView
s
fuente
Respuestas:
UIStackView
Es probablemente el camino a seguir para iOS 9+. No solo maneja la vista oculta, también eliminará espacios y márgenes adicionales si se configura correctamente.fuente
Mi preferencia personal para mostrar / ocultar vistas es crear un IBOutlet con la restricción de ancho o altura adecuada.
Luego actualizo el
constant
valor a0
ocultar, o lo que sea que el valor debería mostrar.La gran ventaja de esta técnica es que se mantendrán las restricciones relativas. Por ejemplo, supongamos que tiene una vista A y una vista B con un espacio horizontal de x . Cuando el ancho de la vista A
constant
se establece en,0.f
entonces la vista B se moverá hacia la izquierda para llenar ese espacio.No es necesario agregar o eliminar restricciones, lo cual es una operación pesada. Simplemente actualizar las restricciones
constant
hará el truco.fuente
La solución de usar una constante
0
cuando está oculta y otra constante si la muestra de nuevo es funcional, pero no es satisfactoria si su contenido tiene un tamaño flexible. Tendría que medir su contenido flexible y establecer una copia de seguridad constante. Esto se siente mal y tiene problemas si el contenido cambia de tamaño debido a eventos del servidor o de la IU.Tengo una mejor solucion.
La idea es establecer una regla de altura 0 para que tenga una alta prioridad cuando ocultamos el elemento para que no ocupe espacio en la distribución automática.
Así es como lo haces:
1. configure un ancho (o alto) de 0 en el generador de interfaces con una prioridad baja.
Interface Builder no gritará sobre conflictos porque la prioridad es baja. Pruebe el comportamiento de altura estableciendo la prioridad en 999 temporalmente (1000 está prohibido mutar programáticamente, por lo que no lo usaremos). El generador de interfaces probablemente ahora gritará sobre restricciones conflictivas. Puede solucionarlos estableciendo prioridades en objetos relacionados a 900 más o menos.
2. Agregue una salida para que pueda modificar la prioridad de la restricción de ancho en el código:
3. Ajuste la prioridad cuando oculte su elemento:
fuente
En este caso, mapeo la altura de la etiqueta Autor a un IBOutlet apropiado:
y cuando configuro la altura de la restricción en 0.0f, conservamos el "relleno", porque la altura del botón Reproducir lo permite.
fuente
Aquí hay muchas soluciones, pero mi enfoque habitual es diferente de nuevo :)
Establezca dos conjuntos de restricciones similares a la respuesta de Jorge Arimany y TMin:
Las tres restricciones marcadas tienen el mismo valor para la constante. Las restricciones marcadas A1 y A2 tienen su prioridad establecida en 500, mientras que la restricción marcada B tiene su prioridad establecida en 250 (o
UILayoutProperty.defaultLow
en el código).Conecte la restricción B a un IBOutlet. Luego, cuando oculta el elemento, solo necesita establecer la prioridad de restricción en alta (750):
constraintB.priority = .defaultHigh
Pero cuando el elemento es visible, vuelva a establecer la prioridad en baja (250):
constraintB.priority = .defaultLow
La ventaja (ciertamente menor) de este enfoque sobre el simple cambio
isActive
de la restricción B es que todavía tiene una restricción de trabajo si el elemento transitorio se elimina de la vista por otros medios.fuente
Subclase la vista y anular
func intrinsicContentSize() -> CGSize
. Simplemente regreseCGSizeZero
si la vista está oculta.fuente
invalidateIntrinsicContentSize
embargo, algunos elementos de la interfaz de usuario necesitan un desencadenante . Puedes hacer eso de forma anuladasetHidden
.Me acabo de enterar de que para que un UILabel no ocupe espacio, debes ocultarlo y configurar su texto en una cadena vacía. (iOS 9)
Conocer este hecho / error podría ayudar a algunas personas a simplificar sus diseños, posiblemente incluso el de la pregunta original, así que pensé que lo publicaría.
fuente
Me sorprende que no haya un enfoque más elegante proporcionado por
UIKit
para este comportamiento deseado. Parece algo muy común querer poder hacer.Dado que conectaba las restricciones
IBOutlets
y configuraba sus constantes para que se0
sintieran desagradables (y causabaNSLayoutConstraint
advertencias cuando su vista tenía subvistas), decidí crear una extensión que ofrezca un enfoque simple y detallado para ocultar / mostrar unUIView
que tenga restricciones de Diseño automáticoSimplemente oculta la vista y elimina las restricciones exteriores. Cuando vuelve a mostrar la vista, vuelve a agregar las restricciones. La única advertencia es que deberá especificar restricciones flexibles de conmutación por error para las vistas circundantes.
Editar Esta respuesta está dirigida a iOS 8.4 y a continuación. En iOS 9, solo usa el
UIStackView
enfoque.fuente
La mejor práctica es que, una vez que todo tenga las restricciones de diseño correctas, agregue una altura o una restricción, según cómo desee que se muevan las vistas circundantes y conecte la restricción a una
IBOutlet
propiedad.Asegúrese de que sus propiedades sean
strong
en el código solo tiene que establecer la constante en 0 y activarla , para ocultar el contenido o desactivarlo para mostrar el contenido. Esto es mejor que perder el tiempo con el valor constante y restaurarlo. No olvides llamar
layoutIfNeeded
después.Si el contenido que se ocultará está agrupado, la mejor práctica es poner todo en una vista y agregar las restricciones a esa vista
Una vez que tenga su configuración, puede probarla en IntefaceBuilder al establecer su restricción en 0 y luego volver al valor original. No olvide verificar otras prioridades de restricciones para que cuando esté oculto no haya ningún conflicto. Otra forma de probarlo es ponerlo a 0 y establecer la prioridad en 0, pero no debe olvidar restaurarlo a la prioridad más alta nuevamente.
fuente
Construyo categoría para actualizar las restricciones fácilmente:
Responda aquí: Ocultar autoejecución UIView: Cómo obtener NSLayoutConstraint existente para actualizar este
fuente
Mi método preferido es muy similar al sugerido por Jorge Arimany.
Prefiero crear múltiples restricciones. Primero cree sus restricciones para cuando la segunda etiqueta sea visible. Cree una salida para la restricción entre el botón y la segunda etiqueta (si está usando objc, asegúrese de que sea fuerte). Esta restricción determina la altura entre el botón y la segunda etiqueta cuando es visible.
Luego, cree otra restricción que especifique la altura entre el botón y la etiqueta superior cuando el segundo botón está oculto. Cree una salida para la segunda restricción y asegúrese de que esta salida tenga un puntero fuerte. Luego desmarque el
installed
casilla de verificación en el generador de interfaces y asegúrese de que la prioridad de la primera restricción sea inferior a la prioridad de esta segunda restricción.Finalmente, cuando oculta la segunda etiqueta, cambie la
.isActive
propiedad de estas restricciones y llamesetNeedsDisplay()
Y eso es todo, sin números mágicos, sin matemáticas, si tiene múltiples restricciones para activar y desactivar, incluso puede usar colecciones de salida para mantenerlas organizadas por estado. (AKA mantiene todas las restricciones ocultas en un OutletCollection y las no ocultas en otro y simplemente repite cada colección para alternar su estado .isActive).
Sé que Ryan Romanchuk dijo que no quería usar restricciones múltiples, pero siento que esto no es microgestión, y es más simple que crea vistas y restricciones dinámicamente mediante programación (que es lo que creo que quería evitar si yo Estoy leyendo la pregunta correcta).
He creado un ejemplo simple, espero que sea útil ...
fuente
También proporcionaré mi solución, para ofrecer variedad.) Creo que crear una salida para el ancho / alto de cada elemento más el espacio es simplemente ridículo y explota el código, los posibles errores y la cantidad de complicaciones.
Mi método elimina todas las vistas (en mi caso, las instancias UIImageView), selecciona cuáles deben agregarse nuevamente y, en un bucle, agrega nuevamente cada una y crea nuevas restricciones. En realidad es muy simple, por favor siga adelante. Aquí está mi código rápido y sucio para hacerlo:
Obtengo un diseño y un espacio limpios y consistentes. Mi código usa Masonry, lo recomiendo: https://github.com/SnapKit/Masonry
fuente
Pruebe BoxView , hace que el diseño dinámico sea conciso y legible.
En tu caso es:
fuente