Establecer restricciones programáticamente

Estoy experimentando con cómo usar UIScrollView. Después de muchos problemas, finalmente me di count. Pero ahora parece que he golpeado otra pega.

En esta aplicación simple, tengo una vista de desplazamiento con y para que funcione, tengo que configurar el espacio inferior de la vista para desplazarse a 0 como se describe aquí y funciona bien. Lo estoy haciendo a través del IB.

Ahora me he encontrado con un escenario en el que tengo que hacer esa parte mediante progtwigción. viewDidLoad el código siguiente en el método viewDidLoad .

 NSLayoutConstraint *bottomSpaceConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]; [self.view addConstraint:bottomSpaceConstraint]; 

Pero parece que no funciona. Muestra el siguiente post en la window de la console y no sé qué hacer con él.

No se pueden satisfacer simultáneamente las restricciones. Probablemente al less una de las restricciones en la siguiente list es una que no desea. Intente esto: (1) mire cada restricción e intente averiguar qué no espera; (2) encuentre el código que agregó la restricción o las restricciones no deseadas y solucionarlo. (Nota: Si está viendo NSAutoresizingMaskLayoutConstraints que no entiende, consulte la documentation de la propiedad UIView traduceAutorizarMaskIntoConstraints) ("", "")

¿Alguien puede decirme cómo hacer esto? También he adjuntado un proyecto de demostración aquí para que pueda tener una mejor idea sobre el problema.

ACTUALIZAR:

En primer lugar, gracias por las respuestas. Usando las forms mencionadas en las respuestas, pude hacerlo funcionar. Sin embargo, en un escenario ligeramente diferente no es así. Ahora estoy intentando cargar una vista en un viewcontroller de manera programática.

Si puedo explicar más. Hay 2 controlleres de vista. El primero es un UITableViewController y el segundo un UIViewController . Dentro de ese un UIScrollView . También hay múltiples UIView y algunas de esas vistas superan la altura normal de la pantalla.

UITableViewController muestra una list de opciones. Según la selección del usuario, se UIView una vista UIView particular del lote en UIViewController con UIScrollView .

En este escenario, el método anterior no funciona. El desplazamiento no está sucediendo. ¿Tengo que hacer algo diferente ya que estoy cargando la vista por separado?

Subí un proyecto de demostración aquí para que puedas verlo en acción. Consulte el Email.xib electrónico.xib y select Correo electrónico en la list de vista de tabla.

Basado en una revisión de su código, algunos comentarios:

  1. Por lo general, no es necesario ajustar las restricciones cuando aparece una vista. Si te encuentras haciendo esto, a menudo significa que no has configurado tu storyboard correctamente (o al less, no de manera eficiente). La única vez que realmente necesita establecer / crear restricciones es (a) está agregando vistas mediante progtwigción (lo que estoy sugiriendo es más trabajo de lo que vale); o (b) necesita hacer algún ajuste en el time de ejecución de las restricciones (vea el tercer punto en el punto 3, a continuación).

  2. No me refiero a trabajar mucho, pero su proyecto tenía un montón de código networkingundante. P.ej

    • Estaba configurando el marco para la vista de desplazamiento, pero eso está gobernado por restricciones, por lo que no hace nada (es decir, cuando se aplican las restricciones, se replaceá cualquier configuration de frame configurada manualmente). En general, en layout automático, no intente cambiar el frame directamente: Edite las restricciones. Pero, de todos modos, no es necesario cambiar las restricciones, por lo que el punto es discutible.

    • Estaba configurando el tamaño de contenido para la vista de desplazamiento, pero en el layout automático, eso también está gobernado por restricciones (de las subvistas), por lo que no era necesario.

    • Estabas estableciendo restricciones para la vista de desplazamiento (que ya eran cero), pero entonces no estabas agregando la vista desde el NIB a la vista de desplazamiento, y también anulando cualquier bash. La pregunta original era cómo cambiar la restricción inferior de la vista de desplazamiento. Pero la restricción inferior para eso ya es cero, por lo que no veo ninguna razón para volverlo a poner a cero.

  3. Sugiero una simplificación más radical de su proyecto:

    • Estás haciendo la vida mucho más difícil consigo mismo al almacenar tus puntos de vista en los NIB. Es mucho más fácil si te mantienes en el mundo del storyboard. Podemos ayudarlo a hacer las cosas del NIB si realmente necesita hacerlo, pero ¿por qué hacer que la vida sea tan dura para usted?

    • Use prototypes celulares para facilitar el layout de las celdas en su table. También puede definir los segues para ir de las celdas a la siguiente escena. Esto elimina cualquier necesidad de escribir cualquier código didSelectRowAtIndexPath o prepareForSegue . Claramente, si tiene algo que necesita para pasar a la siguiente escena, use prepareForSegue , pero nada de lo que haya presentado hasta ahora lo requiere, así que lo he comentado en mis ejemplos.

    • Suponiendo que estuvieras buscando un ejemplo práctico de restricciones que cambian mediante progtwigción, he configurado la escena para que la vista de text cambie su altura mediante progtwigción, en function del text en la vista de text. Como siempre, en lugar de recorrer las restricciones para encontrar el que está en cuestión, al modificar una restricción existente que IB creó para mí, creo que es mucho más eficiente configurar un IBOutlet para la restricción y editar la propiedad constant para la restricción directamente, así que eso es lo que he hecho. Así que configuré el controller de vista para ser el delegado de la vista de text y escribí un textViewDidChange que actualizó la restricción de altura de la vista de text:

       #pragma mark - UITextViewDelegate - (void)textViewDidChange:(UITextView *)textView { self.textViewHeightConstraint.constant = textView.contentSize.height; [self.scrollView layoutIfNeeded]; } 

      Tenga en count que mi vista de text tiene dos limitaciones de altura, una restricción de altura mínima obligatoria y una restricción de prioridad media que cambio más arriba en function de la cantidad de text. El punto principal es que ilustra un ejemplo práctico de cambio de restricciones programáticamente. No debería tener que estropearse con la restricción inferior de scrollview en absoluto, pero esto muestra un ejemplo del mundo real de cuándo es posible que desee ajustar una restricción.


Cuando agrega una vista de desplazamiento en IB, obtendrá automáticamente todas las restricciones que necesita. Probablemente no desee agregar una restricción programáticamente (al less no sin eliminar la restricción inferior existente).

Dos enfoques pueden ser más simples:

  1. Cree un IBOutlet para su restricción inferior existente, por ejemplo, scrollViewBottomConstraint . Entonces solo puedes hacer

     self.scrollViewBottomConstraint.constant = 0.0; 
  2. O cree su vista inicialmente en IB donde la restricción inferior es 0.0 y entonces no tiene que hacer nada programáticamente. Si desea distribuir una vista de desplazamiento larga y sus subvenciones, select el controller, configure sus métricas simuladas de "inferidas" a "formulario libre". A continuación, puede cambiar el tamaño de la vista, configurar las restricciones superior e inferior de la vista de desplazamiento para que sean cero, distribuir todo lo que desee dentro de la vista de desplazamiento y, luego, cuando la vista se presente en time de ejecución, la vista cambiará de tamaño apropiadamente y porque He definido las restricciones superior e inferior de scrollview para que sean 0.0, se networkingimensionará correctamente. Parece un poco extraño en IB, pero funciona como un encanto cuando la aplicación se ejecuta.

  3. Si está decidido a agregar una nueva restricción, puede eliminar mediante progtwigción la restricción inferior anterior o establecer la prioridad inferior de las restricciones inferiores lo más bajo posible, y de esa manera su nueva restricción (con mayor prioridad) tendrá prioridad, y la antigua restricción de background de baja prioridad no se aplicará correctamente.

Pero definitivamente no quieres solo agregar una nueva restricción.

Es posible crear salidas para representar restricciones de layout en su controller de vista. Simplemente select la restricción que desee en el generador de interfaces (por ejemplo, mediante "seleccionar y editar" en el panel de mediciones de la vista que está organizando). Luego diríjase al panel de salidas y arrastre un "New Referencing Outlet" a su file de código (.h o .m). Esto unirá la restricción a una instancia de NSLayoutConstraint que puede acceder desde su controller y se ajustará dinámicamente sobre la marcha (generalmente a través de la propiedad constant , que tiene un nombre pobre porque no es una constante).

introduzca la descripción de la imagen aquí

(Tenga en count que en XCode 6 puede hacer doble clic en la restricción para seleccionarla para la edición).

introduzca la descripción de la imagen aquí

Tenga cuidado al ajustar el layout en el creador de interfaces, sin embargo, ya que puede terminar eliminando la restricción y volver a vincularla al tomastream.

Mirando la información de la console, siento que está creando ambigüedad cuando agrega dos types de restricciones similares.

Entonces, en lugar de crear y agregar una nueva restricción, intente actualizar la restricción anterior que ya está en la matriz de restricciones.

 for(NSLayoutConstraint *constraint in self.view.constraints) { if(constraint.firstAttribute == NSLayoutAttributeBottom && constraint.secondAttribute == NSLayoutAttributeBottom && constraint.firstItem == self.view && constraint.secondItem == self.scrollView) { constraint.constant = 0.0; } } 

Espero que esto ayude

¡Incluso la respuesta de Rob funcionará!

Puede usar https://github.com/SnapKit/Masonry para agregar restricciones mediante progtwigción.

Es el poder de AutoLayout NSLayoutConstraints con una syntax simplificada, encadenable y expresiva. Admite iOS y OSX Auto Layout.

 UIView *superview = self.view; UIView *view1 = [[UIView alloc] init]; view1.translatesAutoresizingMaskIntoConstraints = NO; view1.backgroundColor = [UIColor greenColor]; [superview addSubview:view1]; UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); [superview addConstraints:@[ //view1 constraints [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeTop multiplier:1.0 constant:padding.top], [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeLeft multiplier:1.0 constant:padding.left], [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-padding.bottom], [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeRight multiplier:1 constant:-padding.right],]]; 

en pocas líneas

Aquí están las mismas restricciones creadas usando MASConstraintMaker

 UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); [view1 mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler make.left.equalTo(superview.mas_left).with.offset(padding.left); make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom); make.right.equalTo(superview.mas_right).with.offset(-padding.right); }]; 

O incluso más corto

 [view1 mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(superview).with.insets(padding); }]; 

Haz lo mejor que puedas;