for features ios iphone cocoa-touch interface-builder autolayout

features - xcode version for ios 12



¿Cómo usar el diseño automático para mover otras vistas cuando una vista está oculta? (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. Restrinja las otras vistas con un espacio horizontal hacia esa vista.

Para ocultarlo, 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?

Diseñé mi celda personalizada en IB, la clasifiqué y conecté mis puntos de venta a mi clase personalizada. Tengo tres subvistas en contenido de celdas 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 otras veces solo dos etiquetas. Lo que estoy tratando de hacer es establecer restricciones de esa manera si configuro la propiedad UIView en oculta o la eliminaré de la vista de supervisión, las dos etiquetas se moverán hacia la izquierda. Intenté establecer la restricción principal de UIView en Superview (contenido de celda) para 10px y UILabels las restricciones iniciales de 10 px en la vista siguiente (UIView). Mas tarde en mi codigo

-(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 etiquetas se muevan hacia la izquierda, sin embargo, se mantienen en la misma posición en Cell. Intenté eliminar cell.cdView de superview pero tampoco funcionó. He adjuntado la imagen para aclarar de qué se trata.

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

.....


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

  1. Arrastre y suelte un UIImageView y colóquelo a la izquierda.
  2. Arrastre y suelte un UIView y colóquelo 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 para supervisar en lugar de UIImagView.
  5. Si UIImageView está oculto, establezca la constante de restricción principal en 10 px para supervisar. ELSE, establece la constante de restricción inicial en 10 px + UIImageView.width + 10 px.

He creado una regla de mi propio pulgar. Siempre que tenga que ocultar / mostrar cualquier vista en la cual las restricciones puedan verse afectadas, agregue todas las subvistas afectadas / dependientes dentro de una vista y actualice su constante inicial / posterior / superior / inferior mediante programación.


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 bloqueo que tendría que ser eliminada.

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]; }]; }];


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 que esté un poco adaptado a mis necesidades, pero puede resultarle útil o puede modificarlo y crear su propio ayudante.

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


En este caso, asigno 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.0f, conservamos el "relleno", porque la altura del botón Reproducir lo permite.

cell.authorLabelHeight.constant = 0;


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

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


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, siguen participando 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 significa:

  • Si configura la vista izquierda para que esté oculta, las etiquetas permanecen en su lugar, ya que la vista izquierda todavía está ocupando espacio (aunque no esté visible).
  • Si elimina la vista izquierda, es probable que sus etiquetas queden restringidas de forma ambigua, ya que ya no tendrá restricciones para los bordes izquierdos de sus etiquetas.

Lo que debe hacer es restringir juiciosamente sus etiquetas. Deje sus restricciones existentes (10 puntos de espacio a la otra vista) solo, pero agregue otra restricción: haga que los bordes izquierdos de sus etiquetas queden a 10 puntos del borde izquierdo de su supervisión 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 de 10 puntos obligatoria para la vista izquierda desaparecerá junto con la vista a la que se relaciona, y solo tendrá una restricción de alta prioridad para que las etiquetas estén a 10 puntos de distancia de su vista de supervisión. En el próximo paso de diseño, esto debería hacer que se expandan a la izquierda hasta que llenen el ancho de la vista de supervisión, pero para su espacio alrededor de los bordes.

Una advertencia importante: si alguna vez desea que su vista izquierda vuelva a aparecer en la imagen, no solo tiene que 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 manera de volver a colocar su restricción de espaciado de 10 puntos entre la vista y sus etiquetas cada vez que se muestre esa vista.


Esta es mi otra solución usando la restricción de prioridad. La idea es establecer el ancho a 0.

  1. Crear vista de contenedor (naranja) y establecer ancho.

  2. cree la vista de contenido (rojo) y configure el espacio final 10pt para supervisar (naranja). Observe las restricciones de espacio al final, hay 2 restricciones al final con diferente prioridad. Bajo (= 10) y Alto (<= 10). Esto es importante para evitar la ambigüedad.

  3. Establezca el ancho de la vista naranja en 0 para ocultar la vista.


Esta es una pregunta antigua, pero espero que sirva de ayuda. Viniendo de Android, en esta plataforma tiene un método práctico que es isVisible para ocultarlo de la vista, pero tampoco tiene el marco considerado cuando el autolayout dibuja la vista.

utilizando la extensión y la "extensión" de 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() }


Intenta esto, he implementado el siguiente código,

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

1.ViewController.h Archivo

#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

Espero que esta lógica ayude a alguien.


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

Utilizan la misma clase de UITableViewCell. El inconveniente 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 la vista, las restricciones de actualización, etc.

En general, esta es probablemente una mejor solución ya que son diseños 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];


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


Para aquellos que solo son compatibles con iOS 8+ , hay una nueva propiedad booleana active . Esto ayudará a habilitar dinámicamente solo las restricciones necesarias

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á desactivar la casilla de verificación Installed para una de estas restricciones.

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


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

En la imagen de abajo, la vista roja es el contenedor real de su contenido y tiene un espacio final de 10 puntos para la vista de color naranja (ShowHideView), luego simplemente conecte ShowHideView a IBOutlet y muestre / oculte / elimine programáticamente.

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

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


Use dos UIStackView Horizontal y Vertical, cuando alguna vista de subvista en la pila esté oculta, se moverán otras subvistas de la pila, use Distribución -> Rellenar proporcionalmente para la pila vertical con dos UILabels y es necesario establecer restricciones de altura y anchura para la primera UIView


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


la forma correcta de hacerlo es deshabilitar 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 fuertes salidas para ellos.


UIStackView colocar sus vistas automáticamente cuando la propiedad hidden se cambia 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:

11:48


Para los Googlers: basándose 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 expandirse para cualquier escenario con vistas que contengan.

Aquí hay un ejemplo simple:

En este caso, IBOutlet 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.0f , conservamos el "relleno", porque la altura del botón Reproducir lo permite.