¿Cómo usar el diseño automático para mover otras vistas cuando una vista está oculta?

Diseñé mi Celda personalizada en IB, la subclasé y conecté mis salidas a mi clase personalizada. Tengo tres subvistas en el contenido de la celda que son: UIView (cdView) y dos tags (titleLabel y emailLabel). Dependiendo de los datos disponibles para cada fila, a veces quiero que se muestren UIView y dos tags en mi celda y, a veces, solo dos tags. Lo que bash hacer es establecer restricciones de esa manera si configuro la propiedad UIView como oculta o la eliminaré de la supervista, las dos tags se moverán hacia la izquierda. Traté de establecer la restricción principal de UIView en Superview (contenido de celda) para 10px y UILabels en las limitaciones de 10 px en la vista siguiente (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, permanecen en la misma posición en Cell. Traté de eliminar cell.cdView de Superview, pero tampoco funcionó. He adjuntado una imagen para aclarar de qué se trata.

celda

Sé cómo hacer esto 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 se eliminan u ocultan otras vistas. ¿Es posible hacer esto en IB con diseño automático?

 ..... 

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

  • Las vistas ocultas, aunque no dibujen, aún participan en el diseño automático y generalmente conservan sus marcos , dejando otras vistas relacionadas en su lugar.
  • Al eliminar una vista de su supervista, todas las restricciones relacionadas también se eliminan de esa jerarquía de vistas.

En tu caso, esto probablemente significa:

  • Si configura su vista izquierda para que quede oculta, las tags permanecerán en su lugar, ya que esa vista izquierda aún ocupa espacio (aunque no esté visible).
  • Si elimina su vista izquierda, sus tags probablemente quedarán limitadas ambiguamente, ya que no tiene restricciones para los bordes izquierdos de sus tags.

Lo que tienes que hacer es exprimir demasiado tus tags. Deje sus restricciones existentes (10pts de espacio en la otra vista) solo, pero agregue otra restricción: haga que los bordes izquierdos de sus tags estén a 10pts del borde izquierdo de su superview con una prioridad no requerida (la alta prioridad predeterminada probablemente funcionará bien).

Luego, cuando desee que se muevan hacia la izquierda, elimine la vista izquierda por completo. La restricción obligatoria de 10 puntos a la vista izquierda desaparecerá junto con la vista a la que se refiere, y se le dejará solo una restricción de alta prioridad que las tags estarán a 10pts de su supervista. En el siguiente pase de diseño, esto debería hacer que se expandan hacia la izquierda hasta que llenen el ancho de la supervista, pero para su espacio alrededor de los bordes.

Una advertencia importante: si alguna vez quiere que su vista izquierda vuelva a aparecer en la imagen, no solo debe volver a agregarla a la jerarquía de vistas, sino que también debe restablecer todas sus restricciones al mismo tiempo. Esto significa que necesita una forma de poner su restricción de espaciado de 10 pt entre la vista y sus tags cuando esa vista se muestre de nuevo.

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

Para la vista que desea ocultar, configure una restricción de ancho. Restrinja las otras vistas con una brecha horizontal principal 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 posición.

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

¿Cómo cambiar las restricciones de la etiqueta durante el tiempo de ejecución?

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

La salida de restricción PS debe ser fuerte , no débil

Ejemplo:

 @IBOutlet weak var optionalView: UIView! @IBOutlet var viewIsVisibleConstraint: NSLayoutConstraint! @IBOutlet var viewIsHiddenConstraint: NSLayoutConstraint! func showView() { optionalView.isHidden = false viewIsVisibleConstraint.isActive = true viewIsHiddenConstraint.isActive = false } func hideView() { optionalView.isHidden = true viewIsVisibleConstraint.isActive = false viewIsHiddenConstraint.isActive = true } 

También para corregir un error en el guión gráfico deberá desmarcar la checkbox Installed para una de estas restricciones.

UIStackView (iOS 9+)
Una opción más es ajustar sus vistas en UIStackView . Una vez que la vista está oculta, UIStackView actualizará el diseño automáticamente

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 } 

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

Misterios del diseño automático, parte 1

Mi proyecto utiliza una subclase @IBDesignable personalizada de UILabel (para garantizar la coherencia 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 etiqueta de la subclase participe en Diseño automático, pero no ocupa espacio cuando está oculta.

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

Aquí hay un ejemplo simple:

Captura de pantalla de IB

En este caso, IBOutlet un mapa de la altura de la etiqueta Author a una IBOutlet adecuada:

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

y cuando configuro la altura de la restricción a 0.0f , conservamos el “relleno” porque la altura del botón Reproducir lo permite.

Lo que terminé haciendo fue crear 2 xibs. Uno con la vista izquierda y otra sin ella. Registré ambos en el controlador y luego decidí cuál usar durante cellForRowAtIndexPath.

Usan la misma clase UITableViewCell. La desventaja es que hay una cierta duplicación del contenido entre los xibs, pero estas células son bastante básicas. Lo bueno es que no tengo un montón de código para administrar manualmente quitar vista, actualizar restricciones, etc.

En general, esta es probablemente una solución mejor, ya que son diseños técnicamente diferentes y, por lo tanto, deben 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]; 

conecta la restricción entre uiview y labels como IBOutlet y establece el miembro de prioridad con un valor menor cuando se establece hidden = YES

En este caso, asigno un mapa de la altura de la etiqueta Author a una IBOutlet adecuada:

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

y cuando configuro la altura de la restricción a 0.0f, conservamos el “relleno” porque la altura del botón Reproducir lo permite.

 cell.authorLabelHeight.constant = 0; 

enter image description here enter image description here

Utilice dos UIStackView Horizontal y Vertical, cuando se oculte alguna vista de subvista en la stack, se moverán otras subvistas de 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 enter image description here

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

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

Prueba esto, he implementado el código a continuación,

Tengo una Vista en ViewController en las otras tres vistas agregadas. Cuando cualquier vista esté oculta, otras dos vistas se moverán. Siga los pasos a continuación. ,

1.ViewController.h Archivo

 #import  @interface ViewController : UIViewController @property (strong, nonatomic) IBOutlet UIView *viewOne; @property (strong, nonatomic) IBOutlet UIView *viewTwo; @property (strong, nonatomic) IBOutlet UIView *viewThree; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewOneWidth; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewTwoWidth; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewThreeWidth; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewBottomWidth; @end 

2.ViewController.m

  #import "ViewController.h" @interface ViewController () { CGFloat viewOneWidthConstant; CGFloat viewTwoWidthConstant; CGFloat viewThreeWidthConstant; CGFloat viewBottomWidthConstant; } @end @implementation ViewController @synthesize viewOne, viewTwo, viewThree; - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. /* 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 */ // [viewOne setHidden:NO]; // [viewTwo setHidden:NO]; // [viewThree setHidden:NO]; // [viewOne setHidden:NO]; // [viewTwo setHidden:NO]; // [viewThree setHidden:YES]; // [viewOne setHidden:NO]; // [viewTwo setHidden:YES]; // [viewThree setHidden:NO]; // [viewOne setHidden:NO]; // [viewTwo setHidden:YES]; // [viewThree setHidden:YES]; // [viewOne setHidden:YES]; // [viewTwo setHidden:NO]; // [viewThree setHidden:NO]; // [viewOne setHidden:YES]; // [viewTwo setHidden:NO]; // [viewThree setHidden:YES]; // [viewOne setHidden:YES]; // [viewTwo setHidden:YES]; // [viewThree setHidden:NO]; // [viewOne setHidden:YES]; // [viewTwo setHidden:YES]; // [viewThree setHidden:YES]; [self hideShowBottomBar]; } - (void)hideShowBottomBar { BOOL isOne = !viewOne.isHidden; BOOL isTwo = !viewTwo.isHidden; BOOL isThree = !viewThree.isHidden; viewOneWidthConstant = _viewOneWidth.constant; viewTwoWidthConstant = _viewTwoWidth.constant; viewThreeWidthConstant = _viewThreeWidth.constant; viewBottomWidthConstant = _viewBottomWidth.constant; if (isOne && isTwo && isThree) { // 0 0 0 _viewOneWidth.constant = viewBottomWidthConstant / 3; _viewTwoWidth.constant = viewBottomWidthConstant / 3; _viewThreeWidth.constant = viewBottomWidthConstant / 3; } else if (isOne && isTwo && !isThree) { // 0 0 1 _viewOneWidth.constant = viewBottomWidthConstant / 2; _viewTwoWidth.constant = viewBottomWidthConstant / 2; _viewThreeWidth.constant = 0; } else if (isOne && !isTwo && isThree) { // 0 1 0 _viewOneWidth.constant = viewBottomWidthConstant / 2; _viewTwoWidth.constant = 0; _viewThreeWidth.constant = viewBottomWidthConstant / 2; } else if (isOne && !isTwo && !isThree) { // 0 1 1 _viewOneWidth.constant = viewBottomWidthConstant; _viewTwoWidth.constant = 0; _viewThreeWidth.constant = 0; } else if (!isOne && isTwo && isThree) { // 1 0 0 _viewOneWidth.constant = 0; _viewTwoWidth.constant = viewBottomWidthConstant / 2; _viewThreeWidth.constant = viewBottomWidthConstant / 2; } else if (!isOne && isTwo && !isThree) { // 1 0 1 _viewOneWidth.constant = 0; _viewTwoWidth.constant = viewBottomWidthConstant; _viewThreeWidth.constant = 0; } else if (!isOne && !isTwo && isThree) { // 1 1 0 _viewOneWidth.constant = 0; _viewTwoWidth.constant = 0; _viewThreeWidth.constant = viewBottomWidthConstant; } else if (isOne && isTwo && isThree) { // 1 1 1 _viewOneWidth.constant = 0; _viewTwoWidth.constant = 0; _viewThreeWidth.constant = 0; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end 

enter image description here enter image description here enter image description here

Espero que esta lógica ayude a alguien.

Como no_scene sugirió, definitivamente puede hacer esto cambiando la prioridad de la restricción en tiempo 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 fragmento 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 caso de que esto ayude a alguien, construí una clase de ayuda para usar restricciones de formato visual . Lo estoy usando en mi aplicación actual.

AutolayoutHelper

Puede ser un poco adaptado a mis necesidades, pero puede que le resulte útil o puede querer modificarlo y crear su propio ayudante.

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

Así es como volvería a alinear mis vistas para obtener su solución:

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

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

Esta es una vieja pregunta, pero aún así espero que ayude. Procedente de Android, en esta plataforma tiene un método útil que es isVisible para ocultarlo de la vista, pero que tampoco tiene el marco considerado cuando el diseño automático dibuja la vista.

al usar la extensión y “extender” uiview podría hacer una función similar en ios (no estoy seguro por qué no está en UIKit ya) aquí una implementación en swift 3:

  func isVisible(_ isVisible: Bool) { self.isHidden = !isVisible self.translatesAutoresizingMaskIntoConstraints = isVisible if isVisible { //if visible we remove the hight constraint if let constraint = (self.constraints.filter{$0.firstAttribute == .height}.first){ self.removeConstraint(constraint) } } else { //if not visible we add a constraint to force the view to have a hight set to 0 let height = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal , toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 0) self.addConstraint(height) } self.layoutIfNeeded() } 

la forma correcta de hacerlo es desactivar restricciones con isActive = false. Sin embargo, tenga en cuenta que la desactivación de una restricción lo elimina y lo libera, por lo que debe tener salidas sólidas para ellos.

Usaré stackview horizontal. Puede eliminar el marco cuando la subvista está oculta.

En la imagen a continuación, la vista roja es el contenedor real para su contenido y tiene 10pt de espacio libre en la vista naranja (ShowHideView), luego simplemente conecte ShowHideView a IBOutlet y muéstrelo / oculte / elimine programáticamente.

  1. Esto es cuando la vista es visible / instalada.

la vista es visible

  1. Esto es cuando la vista está oculta / no instalada.

la vista está oculta / eliminada

Esta es otra solución que usa restricción de prioridad. La idea es establecer el ancho a 0.

  1. crear vista de contenedor (naranja) y establecer ancho. enter image description here

  2. cree una vista de contenido (rojo) y establezca el espacio posterior 10pt en la supervista (naranja). Observe las restricciones de espacio final, hay 2 restricciones finales con diferente prioridad. Bajo (= 10) y Alto (<= 10). Esto es importante para evitar la ambigüedad. enter image description here

  3. Establezca el ancho de la vista naranja en 0 para ocultar la vista. enter image description here

    Intereting Posts