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


He diseñado mi celda personalizada en IB, la he subclasificado y he conectado mis enchufes a mi clase personalizada. Tengo tres subviews en el contenido de la celda que son: UIView (cdView) y dos etiquetas (titleLabel y emailLabel). Dependiendo de los datos disponibles para cada fila, a veces quiero tener UIView y dos etiquetas mostradas en mi celda y, a veces, solo dos etiquetas. Lo que estoy tratando de hacer es establecer restricciones de esa manera si establezco la propiedad UIView en oculto o la eliminaré de superview las dos etiquetas se moverá a la izquierda. Intenté establecer UIView leading constraint a Superview (Cell content) para 10px y UILabels leading Constraints para 10 px a la siguiente vista (UIView). Más adelante 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 escondiendo mi celular.cdView y me gustaría que las etiquetas se muevan a la izquierda, sin embargo, permanecen en la misma posición en la celda. Traté de quitar a Cell.cdView de superview pero tampoco funcionó. He adjuntado la imagen para aclarar de qué se trata.

celular

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 subviews se muevan dinámicamente si se eliminan u ocultan otras vistas. ¿Es posible hacer esto en IB con auto-layout?

.....
Author: Cœur, 2013-08-05

19 answers

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

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

En su caso, esto probablemente medios:

  • Si configura la vista izquierda para que se oculte, las etiquetas permanecen en su lugar, ya que esa vista izquierda sigue ocupando espacio (aunque no sea visible).
  • Si elimina la vista izquierda, sus etiquetas probablemente se dejarán con restricciones ambiguas, ya que ya no tiene restricciones para los bordes izquierdos de sus etiquetas.

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

Luego, cuando desee que se muevan a la izquierda, elimine la vista izquierda por completo. La restricción obligatoria de 10pt a la vista izquierda desaparecerá junto con la vista a la que se relaciona, y se quedará con una restricción de alta prioridad que las etiquetas estén a 10pts de su supervisión. En el siguiente pase de diseño, este debe hacer que se expandan a la izquierda hasta que llenen el ancho de la vista superior, pero para su espaciado alrededor de los bordes.

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

 329
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-05 19:29:19

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. Restringir los otros puntos de vista con una brecha horizontal principal a ese punto de 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 su posición.

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

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

 186
Author: Max MacLeod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:36

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

P.d. Salida de restricción 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 storyboard, deberá desmarcar la casilla Installed para una de estas restricciones.

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

 67
Author: Silmaril,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-16 16:24:31

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

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

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

Misterios del Diseño automático, Parte 1

 48
Author: FFire,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-23 15:32:47

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

 14
Author: Robert Atkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-24 13:43:57

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

He aquí un ejemplo sencillo:

IB Captura de pantalla

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

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

Y cuando establezco la altura de la restricción a 0.0f, conservamos el "relleno", porque la altura del botón Play lo permite.

 11
Author: jterry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-19 00:55:45

Lo que terminé haciendo fue crear 2 xibs. Uno con la vista izquierda y otro 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 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 administrar manualmente la eliminación de vistas, la actualización de restricciones,etc.

En general, esto es probablemente una mejor solución, 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];
 7
Author: Michael Peterson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-05 22:29:51

Conecte la restricción entre uiview y labels como IBOutlet y establezca priority member en un valor menor cuando se establezca hidden = YES

 6
Author: n0c,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-06 15:11:20

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

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

Y cuando establezco la altura de la restricción a 0.0 f, conservamos el "relleno", porque la altura del botón de reproducción lo permite.

cell.authorLabelHeight.constant = 0;

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

 5
Author: Mitul Marsoniya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-01 07:56:37

Use dos UIStackView Horizontal y Vertical, cuando alguna vista subview en la pila está oculta, otras subviews de la pila se moverán, use Distribution - > Fill Proportionally para la pila Vertical con dos UILabels y necesite establecer las constantes de ancho y alto para la primera UIViewintroduzca la descripción de la imagen aquí

 4
Author: Roman Solodyashkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-05 15:32:03

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

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

 1
Author: testing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-11 13:08:35

Prueba esto, he implementado el siguiente código,

Tengo una vista en ViewController en la que se agregaron otras tres vistas, Cuando cualquier vista está oculta otras dos vistas se moverán,Siga los pasos a continuación. ,

1.ViewController.h File

#import <UIKit/UIKit.h>
@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

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

Espero que esta lógica ayude a alguien.

 1
Author: Jaywant Khedkar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-08 11:31:13

Como sugirió no_scene, 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 bloqueo que tendría que ser eliminado.

Aquí hay un fragmento usando 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];
        }];
    }];
 0
Author: skensell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-10 08:40:02

En caso de que esto ayude a alguien, construí una clase helper para usar formato visual restricciones. 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 que desee modificarlo y crear su propio ayudante.

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

 0
Author: Ferran Maylinch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:56

Así es como me gustaría volver a alinear mis uiviews para obtener su solución:

  1. Arrastre una vista UIImageView y colóquela a la izquierda.
  2. Arrastre una UIView y colóquela a la derecha de UIImageView.
  3. Arrastre y suelte dos UILabels dentro de esa UIView cuyas restricciones iniciales y finales son cero.
  4. Establezca la restricción inicial de UIView que contiene 2 etiquetas en superview en lugar de UIImagView.
  5. SI UIImageView está oculto, establezca la constante de restricción principal en 10 px a superview. DE lo contrario, establezca la constante de restricción principal en 10 px + UIImageView.ancho + 10 px.

He creado una regla de oro propia. Siempre que tenga que ocultar / mostrar cualquier uiview cuyas restricciones podrían verse afectadas, agregue todas las subviews afectadas / dependientes dentro de una uiview y actualice su constante de restricción inicial / final / superior / inferior mediante programación.

 0
Author: Deepak Thakur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-12 14:34:07

Esta es una vieja pregunta, pero aún así espero que ayude. Viniendo de Android, en esta plataforma tienes un práctico método isVisible para ocultarlo de la vista pero también no tener el marco considerado cuando el diseño automático dibuja la vista.

Usando extension y" extend " uiview podría hacer una función similar en ios (no estoy seguro de 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()
    }
 0
Author: Vincent Choubard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-23 16:15:32

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

 0
Author: Hogdotmac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-06 13:15:54

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

En la imagen de abajo, la vista roja es el contenedor real para su contenido y tiene 10pt de espacio final a la vista superior naranja (ShowHideView), a continuación, solo tiene que conectar ShowHideView a IBOutlet y mostrar/ocultar/eliminar programáticamente.

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

la vista es visible

  1. Esto es cuando la vista es oculto / no instalado.

la vista está oculta / eliminada

 0
Author: mnemonic23,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-05 09:45:07

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

  1. Cree la vista de contenedor (naranja) y establezca el ancho. introduzca la descripción de la imagen aquí

  2. Cree la vista de contenido (rojo) y establezca el espacio final de 10pt en superview (naranja). Observe las restricciones de espacio al final, hay 2 restricciones al final con diferente prioridad. Bajo (=10) y Alto (introduzca la descripción de la imagen aquí

  3. Establecer el ancho de la vista naranja en 0 a oculta la vista. introduzca la descripción de la imagen aquí

 0
Author: mnemonic23,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-05 10:03:56