Use autolayout para configurar UIView dinámica para que coincida con la vista del contenedor.

Tengo una UIView en IB que tiene otra UIView dentro, que estoy usando como una vista de contenedor. En el código, creo tres vistas diferentes y luego animo la apropiada en la vista del contenedor dependiendo del estado de la aplicación. Solo una de las tres vistas diferentes será válida en un momento dado. El problema es que cuando ejecuto diferentes iPhone en el simulador, mis nuevas subvistas no se escalan para que coincidan con la vista de contenedor. Estoy usando autolayout Para propósitos de testing, he configurado mis subvistas para que solo sean un button grande que tiene todos sus bordes restringidos a la supervisión. Y la vista de contenedor también tiene sus bordes restringidos a su supervisión. Lo que quiero es que la subvista coincida con la vista del contenedor. es decir, el button estira todo el tamaño de la vista del contenedor. Cuando se ejecuta en diferentes iPhones, el tamaño de la vista de contenedor y, por lo tanto, la subvista debe escalar proporcionalmente en relación con los tamaños de pantalla de diferentes iPhone.

A continuación se muestra el código que utilizo para iniciar mi subvista y configurar sus restricciones relativas a la vista del contenedor.

UIView *containerView = self.subView; UIView *newSubview = [[mySubview alloc] init]; [newSubview setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.containerView addSubview:newSubview]; [self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]]; [self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]]; [self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]]; [self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]]; 

Simplemente no puedo conseguir esto funcionar. Soy bastante nuevo en autolayout y no estoy seguro de qué estoy haciendo mal y me gustaría dejar de golpearme la cabeza contra esta panetworking. Cualquier ayuda sería estupenda. 🙂


************* INFORMACIÓN ADICIONAL **************


Lo siento, no he indicado mi problema tan claramente como podría haberlo hecho. Así que aquí hay más información con capturas de pantalla. Primero, describiré lo que he hecho en key.

En didFinishLaunchingWithOptions en AppDelegate.m, creo MyViewController así,

 self.myViewController = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil]; 

En viewDidLoad en MyViewController.m, creo mySubview y lo agrego a mi containerView y crea restricciones para esto,

 UIView *containerView = self.containerView; UIView *mySubview = [[MySubview alloc] init]; [mySubview setTranslatesAutoresizingMaskIntoConstraints:NO]; [containerView addSubview:mySubview]; NSDictionary *views = NSDictionaryOfVariableBindings(mySubview); [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[mySubview]|" options:0 metrics:nil views:views]]; [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[mySubview]|" options:0 metrics:nil views:views]]; 

Y finalmente en init en MySubview.h agrego la punta de testing como una subvista como esta,

 - (id)init { if(self = [super init]) { NSArray *nibArray = [[NSBundle mainBundle]loadNibNamed:@"MySubview" owner:self options:nil]; [self addSubview:[nibArray objectAtIndex:0]]; } return self; } 

Un par de cosas para notar que pueden ayudar,

En MyViewController.xib, tengo una UIView que estoy usando como containerView. Tiene un IBOutlet a UIView * containerView y es el que se menciona anteriormente. Las únicas restricciones que tengo para el contenedorView en IB son, el espacio inicial, el final y el inferior para Superview y el espacio superior para Superview = 44.

Para MySubview.xib, la altura y el ancho son 300, (no se utilizan restricciones para la altura o el ancho). Siento que estos tamaños no deberían importar, ya que se supone que mySubview se ve restringido a containerView.

En MySubview.xib tengo 3 objects, topButton: height = 29, middleView: height = 242 y bottomButton: height = 29. (ver la image adjunta). TopButton tiene las principales, finales y superiores a las restricciones Superview y una restricción inferior a MiddleView. MiddleView tiene las principales y posteriores a las restricciones de Superview y una restricción de top to topButton y una restricción de bottom to bottomButton. Y, finalmente, el button inferior tiene las restricciones principales, finales e inferiores a Superview y una restricción superior a media.

Lo que quiero que ocurra es que mySubview se ajuste a la escala de containerView ya que las restricciones se han creado y agregado, pero en cambio, mySubview se hace muy grande y clips containerView.

Aquí hay algunas capturas de pantalla:

MyViewController.xib, el rectángulo azul debajo del título es mi vista de contenedor y tiene el IBOutlet containerView.

http://i33.tinypic.com/14o3lmd.png

MySubview.xib

http://i37.tinypic.com/344ukk3.png

Y, por último, el resultado, que son incorrectos.

http://i34.tinypic.com/nbyqua.png

En su lugar, querría esto, que he fingido solo para get la captura de pantalla.

En el iPhone 4,

http://tinypic.com/r/w9fp5e/4

En el iPhone 5,

http://tinypic.com/r/2z8nldv/4

Como puede ver en las capturas de pantalla falsas, mySubview se ajusta al contenedorView, incluso cuando containerView escala un poco para ajustar los diferentes tamaños de pantalla del teléfono.

Espero no haber ido al agua con la información. Cualquier ayuda sería estupenda. Siento que estoy cerca, pero debo perder un paso key. Grrrrr.

Un par de pensamientos:

  1. Esto básicamente se ve bien, aparte de algunas rarezas de nombre de variable: su variable local, containerView es igual a self.subView , pero todas sus otras líneas se refieren a una variable diferente, una propiedad de class, self.containerView . ¿Omitió una línea donde estableció esa propiedad de class containerView ? Pero cuando solucioné eso, su código funcionó bien para mí.

  2. Asegúrese de que no está intentando mirar el frame inmediatamente después de establecer las restricciones, ya que el cambio aún no se reflejará en la configuration del frame . Puede hacer un [containerView layoutIfNeeded]; si quiere forzarla a retransmitir todo basado en las restricciones. Además, si desea confirmar la configuration del frame , es mejor aplazar la búsqueda de esos valores hasta después de viewDidAppear (es decir, viewDidLoad es demasiado pronto en el process de construcción de la vista).

  3. Un pequeño ajuste en su código (y no relacionado con su problema), pero cuando estoy estableciendo las restricciones dentro de una vista de contenedor, a menudo estableceré no solo NSLayoutAttributeTop y NSLayoutAttributeLeading , como lo hizo, sino también NSLayoutAttributeBottom y NSLayoutAttributeTrailing (en lugar de NSLayoutAttributeWidth y NSLayoutAttributeHeight ). Realiza lo mismo que su código, pero cuando usa valores distintos de cero, es una fracción más intuitiva.

    De todos modos, acabo de hacer el siguiente código, permutación suya, y funciona bien:

     - (IBAction)didTouchUpInsideAddView:(id)sender { UIView *containerView = self.containerView; UIView *newSubview = [[UIView alloc] initWithFrame:CGRectZero]; // initializing with CGRectZero so we can see it change newSubview.translatesAutoresizingMaskIntoConstraints = NO; newSubview.backgroundColor = [UIColor lightGrayColor]; [containerView addSubview:newSubview]; [containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]]; [containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0]]; [containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]]; [containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0]]; // the frame is still `CGRectZero` at this point NSLog(@"newSubview.frame before = %@", NSStringFromCGRect(newSubview.frame)); // if not doing this in `viewDidLoad` (eg you're doing this in // `viewDidAppear` or later), you can force `layoutIfNeeded` if you want // to look at `frame` values. Generally you don't need to do this unless // manually inspecting `frame` values or when changing constraints in a // `animations` block of `animateWithDuration`. [containerView layoutIfNeeded]; // everything is ok here NSLog(@"containerView.bounds after = %@", NSStringFromCGRect(containerView.bounds)); NSLog(@"newSubview.frame after = %@", NSStringFromCGRect(newSubview.frame)); } 
  4. Puede simplificar ese código un poco utilizando el lenguaje de formatting visual , por ejemplo:

     NSDictionary *views = NSDictionaryOfVariableBindings(newSubview); [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[newSubview]|" options:0 metrics:nil views:views]]; [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[newSubview]|" options:0 metrics:nil views:views]]; 

    Me resulta más fácil corregir las restricciones utilizando el lenguaje de formatting visual. Un poco less propenso a errores (para mí, al less). Sin embargo, hay algunas restricciones que no pueden representarse en el lenguaje de formatting visual, en cuyo caso reparo en la syntax que usted describe.

  5. En su pregunta revisada, nos muestra un método init para su subvista, que hace otro addSubview . Tienes que establecer restricciones allí también. En resumidas counts, donde sea que addSubview , debe establecer sus restricciones, por ejemplo,

     - (id)init { if(self = [super init]) { NSArray *nibArray = [[NSBundle mainBundle]loadNibNamed:@"MySubview" owner:self options:nil]; UIView *subview = [nibArray objectAtIndex:0]; subview.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:subview]; NSDictionary *views = NSDictionaryOfVariableBindings(subview); [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[subview]|" options:0 metrics:nil views:views]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|" options:0 metrics:nil views:views]]; } return self; } 

En el problema indica que "mis nuevas subvistas no tienen escala para que coincidan con la vista de contenedor", pero de la descripción que creo que son, el problema es que la vista de contenedor no tiene un tamaño fijo.

Creo que si configura la vista de contenedor para que tenga un ancho y alto fijo que debería hacerlo, también puede que necesite llamar

 [self.containerView layoutSubviews] 

para forzar un cambio de tamaño después de actualizar las restricciones.

También sugiero que cambie a usar el formatting basado en text, podría cambiar sus líneas anteriores por algo así como "H: | [newSubview] |" y "V: | [newSubview] |"