¿Cómo usar el layout automático para mover otras vistas cuando se oculta una vista?

He diseñado mi Cell personalizada en IB, la subclasé y conecté mis salidas a mi class personalizada. Tengo tres subvenciones en contenido celular que son: UIView (cdView) y dos tags (titleLabel y emailLabel). Dependiendo de los datos disponibles para cada fila, a veces quiero tener UIView y dos tags en mi celda y, a veces, solo dos tags. Lo que trato de hacer es establecer restricciones de esa manera si configuro la propiedad UIView en oculta o la quitaré de la supervisión, las dos tags se moverán hacia la izquierda. Traté de establecer la restricción principal de UIView a Superview (contenido de celda) para 10px y restricciones de liderazgo de UILabels para 10 px a la siguiente vista (UIView). Más tarde en mi código

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath { ... Record *record = [self.records objectAtIndex:indexPath.row]; if ([record.imageURL is equalToString:@""]) { cell.cdView.hidden = YES; } 

Estoy ocultando mi cell.cdView y me gustaría que las tags se muevan hacia la izquierda, sin embargo, se quedan en la misma position en Cell. Traté de eliminar cell.cdView de Superview, pero tampoco funcionó. He adjuntado una image para aclarar de qué estoy hablando.

celda

Sé cómo hacerlo programáticamente y no estoy buscando esa solución. Lo que quiero es establecer restricciones en IB y espero que mis subvistas se muevan dinámicamente si otras vistas se eliminan u ocultan. ¿Es posible hacer esto en IB con layout automático?

 ..... 

Es posible, pero tendrás que hacer un poco más de trabajo. Hay un par de cosas conceptuales para salir del path primero:

  • Las vistas ocultas, aunque no dibujan, aún participan en el layout automático y generalmente conservan sus frameworks , dejando otras vistas relacionadas en sus lugares.
  • Al eliminar una vista de su vista de supervisión, todas las restricciones relacionadas también se eliminan de esa jerarquía de vista.

En su caso, esto probablemente significa:

  • Si configura su vista izquierda para que se oculte, las tags permanecerán en su lugar, ya que la vista izquierda aún ocupa espacio (aunque no esté visible).
  • Si elimina la vista izquierda, es probable que sus tags queden limitadas de forma ambigua, ya que ya no tendrá restricciones para los bordes izquierdos de sus tags.

Lo que debe hacer es juiciosamente sobre-restringir sus tags. Deje sus restricciones existentes (10pts espacio para la otra vista) solo, pero agregue otra restricción: haga que los bordes izquierdos de sus tags queden a 10pts del borde izquierdo de su supervisión con una prioridad no requerida (la alta prioridad pnetworkingeterminada probablemente funcione bien).

Luego, cuando quiera que se muevan a la izquierda, quite la vista izquierda por completo. La restricción obligatoria de 10pt a la vista izquierda desaparecerá junto con la vista a la que se refiere, y solo le quedará una restricción de alta prioridad para que las tags queden a 10pts de distancia de su supervisión. En el próximo pase de layout, esto debería hacer que se expandan a la izquierda hasta que llenen el ancho de la vista de supervisión, pero para su espaciado alnetworkingedor de los bordes.

Una advertencia importante: si alguna vez desea que la vista izquierda vuelva a aparecer en la image, no solo tiene que volver a agregarla a la jerarquía de vista, sino que también debe restablecer todas sus restricciones al mismo time. Esto significa que necesita una manera de poner su restricción de espaciamiento de 10pt entre la vista y sus tags cuando esa vista se muestre nuevamente.

Agregar o eliminar restricciones durante el time de ejecución es una operación pesada que puede afectar el performance. Sin embargo, hay una alternativa más simple.

Para la vista que desea ocultar, configure una restricción de ancho. Restringir las otras vistas con una brecha horizontal inicial a esa vista.

Para ocultar, actualice el .constant de la restricción de ancho a 0.f. Las otras vistas se moverán automáticamente a la izquierda para asumir la position.

Vea mi otra respuesta aquí para más detalles:

¿Cómo cambiar las restricciones de label durante el time de ejecución?

Para aquellos que solo admiten iOS 8+, hay una nueva propiedad booleana activa . Ayudará a habilitar las restricciones necesarias solo dinámicamente

La salida de PS Constraint debe ser fuerte , no débil

UIStackView reposiciona sus vistas automáticamente cuando se cambia la propiedad hidden en cualquiera de sus subvistas (iOS 9+).

 UIView.animateWithDuration(1.0) { () -> Void in self.mySubview.hidden = !self.mySubview.hidden } 

Salta a las 11:48 en este video de WWDC para una demostración:

Mysteries of Auto Layout, Parte 1

Mi proyecto utiliza una subclass @IBDesignable personalizada de UILabel (para garantizar la coinheritance en el color, la fuente, las inserciones, etc.) y he implementado algo como lo siguiente:

 override func intrinsicContentSize() -> CGSize { if hidden { return CGSizeZero } else { return super.intrinsicContentSize() } } 

Esto permite que la subclass de label participe en el layout automático, pero no ocupa espacio cuando está oculto.

Para los Googlers: basándose en la respuesta de Max, para resolver el problema de relleno que muchos han notado, simplemente aumenté la altura de la label y usé esa altura como el separador en lugar del relleno real. Esta idea podría ampliarse para cualquier escenario con vistas contenidas.

Aquí hay un ejemplo simple:

Captura de pantalla de IB

En este caso, IBOutlet la altura de la label de Autor a un IBOutlet apropiado:

 @property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight; 

y cuando establezco la altura de la restricción a 0.0f , preservamos el "relleno", porque la altura del button Play lo permite.

conecte la restricción entre uiview y las tags como IBOutlet y establezca el miembro de prioridad en un valor menor cuando se establezca oculto = SÍ

Lo que terminé haciendo fue crear 2 xibs. Una con la vista izquierda y otra sin ella. Me registré en el controller y luego decidí qué usar durante cellForRowAtIndexPath.

Utilizan la misma class UITableViewCell. La desventaja es que hay una cierta duplicación del contenido entre los xibs, pero estas celdas son bastante básicas. La ventaja es que no tengo un montón de código para gestionar manualmente la eliminación de la vista, la actualización de las restricciones, etc.

En general, esta es probablemente una solución mejor ya que son layouts técnicamente diferentes y, por lo tanto, deberían tener diferentes xibs.

 [self.table registerNib:[UINib nibWithNibName:@"TrackCell" bundle:nil] forCellReuseIdentifier:@"TrackCell"]; [self.table registerNib:[UINib nibWithNibName:@"TrackCellNoImage" bundle:nil] forCellReuseIdentifier:@"TrackCellNoImage"]; TrackCell *cell = [tableView dequeueReusableCellWithIdentifier:(appDelegate.showImages ? @"TrackCell" : @"TrackCellNoImage") forIndexPath:indexPath]; 

En este caso, asigno la altura de la label de Autor a un IBOutlet apropiado:

 @property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight; 

y cuando establezco la altura de la restricción a 0.0f, preservamos el "relleno", porque la altura del button Play lo permite.

 cell.authorLabelHeight.constant = 0; 

introduzca la descripción de la imagen aquí introduzca la descripción de la imagen aquí

Use dos UIStackView Horizontal y Vertical, cuando se oculta una vista de subview en la stack, se moverán otras subidas de la stack, use Distribución -> Rellenar Proporcionalmente para la stack Vertical con dos UILabels y necesite establecer constantes de ancho y alto para la primera UIView introduzca la descripción de la imagen aquí

Como sugirió no_scene, definitivamente puede hacer esto cambiando la prioridad de la restricción en time de ejecución. Esto fue mucho más fácil para mí porque tenía más de una vista de locking que debería eliminarse.

Aquí hay un fragment con ReactiveCocoa:

 RACSignal* isViewOneHiddenSignal = RACObserve(self.viewModel, isViewOneHidden); RACSignal* isViewTwoHiddenSignal = RACObserve(self.viewModel, isViewTwoHidden); RACSignal* isViewThreeHiddenSignal = RACObserve(self.viewModel, isViewThreeHidden); RAC(self.viewOne, hidden) = isViewOneHiddenSignal; RAC(self.viewTwo, hidden) = isViewTwoHiddenSignal; RAC(self.viewThree, hidden) = isViewThreeHiddenSignal; RAC(self.viewFourBottomConstraint, priority) = [[[[RACSignal combineLatest:@[isViewOneHiddenSignal, isViewTwoHiddenSignal, isViewThreeHiddenSignal]] and] distinctUntilChanged] map:^id(NSNumber* allAreHidden) { return [allAreHidden boolValue] ? @(780) : @(UILayoutPriorityDefaultHigh); }]; RACSignal* updateFramesSignal = [RACObserve(self.viewFourBottomConstraint, priority) distinctUntilChanged]; [updateFramesSignal subscribeNext:^(id x) { @strongify(self); [self.view setNeedsUpdateConstraints]; [UIView animateWithDuration:0.3 animations:^{ [self.view layoutIfNeeded]; }]; }]; 

En mi caso, establezco la constante de la restricción de altura a 0.0f y también establezco la propiedad hidden en YES .

Para mostrar la vista (con las subvistas) nuevamente hice lo contrario: establecí la constante de altura en un valor distinto de cero y establecí la propiedad hidden en NO .

En caso de que esto ayude a alguien, construí una class auxiliar para usar restricciones de formatting visual . Lo estoy usando en mi aplicación actual.

AutolayoutHelper

Puede ser un poco adaptado a mis necesidades, pero puede que te resulte útil o quizás quieras modificarlo y crear tu propio ayudante.

Tengo que agradecerle a Tim por su respuesta anterior , esta respuesta sobre UIScrollView y también este tutorial .

A continuación, le mostramos cómo podría volver a alinear mis uiviews para get su solución:

  1. Arrastra un UIImageView y colócalo a la izquierda.
  2. Arrastra una UIView y colocala a la derecha de UIImageView.
  3. Arrastrar y soltar dos UILabels dentro de esa UIView cuyas restricciones iniciales y finales son cero.
  4. Establezca la restricción principal de UIView que contenga 2 tags a supervisión en lugar de UIImagView.
  5. Si UIImageView está oculto, establezca la constante de restricción principal a 10 px para supervisar. ELSE, establezca la constante de restricción principal a 10 px + UIImageView.width + 10 px.

Creé una regla de pulgar por mi count. Siempre que tenga que esconder / mostrar cualquier uiview cuyas restricciones puedan verse afectadas, agregue todas las subvisualizaciones afectadas / dependientes dentro de uiview y actualice su constante de restricción principal / posterior / superior / inferior mediante progtwigción.