ios uiview autolayout resize

ios - Use autolayout para configurar UIView dinámico para que coincida con la vista de contenedor



resize (2)

Tengo un UIView en IB que tiene otro UIView dentro, que estoy usando como 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 coincidir con la vista del contenedor. Estoy usando autolayout. Para propósitos de prueba, he configurado mis subvistas para que sean solo un botón grande que tiene todos sus bordes restringidos a la supervisión. Y la vista del 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 botón estira todo el tamaño de la vista del contenedor. Cuando se ejecuta en diferentes iPhones, el tamaño de la vista del contenedor y, por lo tanto, la subvista debe escalar proporcionalmente en relación con los diferentes tamaños de pantalla del iPhone.

A continuación se muestra el código que utilizo para iniciar mi subvista y configurar sus restricciones en relación con 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]];

Parece que no puedo hacer que esto funcione. Soy bastante nuevo en autolayout y no estoy seguro de lo que estoy haciendo mal y me gustaría dejar de golpear mi cabeza contra esta pared. Cualquier ayuda sería estupenda. :)

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

Lo siento, no he expresado 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 código.

En didFinishLaunchingWithOptions en AppDelegate.m, creo MyViewController como este,

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

En viewDidLoad en MyViewController.m, creo mySubview y lo agrego a mi containerView y creo restricciones para él de esta manera,

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 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 a tener en cuenta 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 containerView en IB son, espacio inicial, final y inferior a Superview y superior espacio a Superview = 44.

Para MySubview.xib, el alto y el ancho son 300, (no se usan restricciones para el alto o el ancho). Siento que estos tamaños no deberían importar ya que se supone que mySubview se debe restringir a containerView.

En MySubview.xib tengo 3 objetos, topButton: height = 29, middleView: height = 242 y bottomButton: height = 29. (ver imagen adjunta) TopButton tiene restricciones iniciales y finales de Superview y una restricción de inferior a middleView. MiddleView tiene restricciones iniciales y finales en Superview y una restricción top to topButton y una restricción bottom to bottomButton. Y, por último, bottomButton tiene restricciones iniciales, finales y inferiores a Superview y una restricción superior a middleView.

Lo que quiero que suceda es que mySubview se ajuste a la escala de containerView, ya que se crearon y agregaron restricciones, pero en su lugar, mySubview se hace muy grande y los clips se ven en contenedor.

Aquí hay algunas capturas de pantalla:

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

MySubview.xib

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

En su lugar, me gustaría esto, que he falsificado sólo para obtener la captura de pantalla.

En el iPhone 4,

En el iPhone 5,

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

Espero no haber ido por la borda con la información. Cualquier ayuda sería estupenda. Siento que estoy cerca, pero me falta un paso clave. Grrrrr.


En el problema, usted indica que "mis nuevas subvistas no se escalan para que coincidan con la vista del contenedor", pero a juzgar por la descripción, el problema es que su vista del contenedor no tiene un tamaño fijo.

Creo que si configura su vista de contenedor para que tenga un ancho y una altura fijos que deban hacerlo, es posible que también deba llamar

[self.containerView layoutSubviews]

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

También te sugeriría que cambies el uso del formato basado en texto, podrías cambiar tus líneas arriba por algo como "H: | [newSubview] |" y "V: | [newSubview] |"


Un par de pensamientos:

  1. Básicamente, esto se ve bien, aparte de algunas rarezas de nombres de variables: su variable local, containerView es igual a self.subView , pero todas sus otras líneas se refieren a una variable diferente, una propiedad de clase, self.containerView . ¿Omitiste una línea donde estableciste esa propiedad de clase containerView ? Pero cuando lo arreglé, tu 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 configuración del frame . Puedes hacer un [containerView layoutIfNeeded]; si quieres forzarlo a retransmitir todo basado en las restricciones. Además, si desea confirmar la configuración del frame , es mejor aplazar la viewDidAppear esos valores hasta después de viewDidAppear (es decir, viewDidLoad es demasiado temprano en el proceso de construcción de la vista).

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

    De todos modos, acabo de hacer el siguiente código, permutación del tuyo, 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` (e.g. 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 un poco ese código utilizando el lenguaje de formato 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 hacer las restricciones correctamente usando el lenguaje de formato visual. Un poco menos propenso a errores (para mí, al menos). Sin embargo, hay algunas restricciones que no se pueden representar en el lenguaje de formato visual, en cuyo caso recurro a la sintaxis que describe.

  5. En su pregunta revisada, nos muestra un método de init para su subvista, que hace otro addSubview . Tienes que establecer restricciones allí también. En pocas palabras, 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; }