¿Cómo puedo cambiar la prioridad de las restricciones en tiempo de ejecución?

86

Tengo una vista que tiene altura dinámica y estoy tratando de cambiar esta prioridad de altura de vista en tiempo de ejecución.

Aquí está mi parte del código;

if (index == 0) {

    surveyViewHeightConstraint.constant = 0;
    surveyViewHeightConstraint.priority = 1000;

} else if (index == 1) {

    surveyViewHeightConstraint.constant = 163;
    surveyViewHeightConstraint.priority = 500;

}

Estoy cambiando el índice con la acción de un botón. Cuando ejecuto este código, obtengo este error:

*** Assertion failure in -[NSLayoutConstraint setPriority:], /SourceCache/Foundation/Foundation-1141.1/Layout.subproj/NSLayoutConstraint.m:174

¿Cuál es mi error aquí?

Le'Kirdok
fuente

Respuestas:

172

Como se indica en NSLayoutConstraintla referencia de la clase :

Las prioridades no pueden cambiar de no requeridas a requeridas, o de requeridas a no requeridas. Se lanzará una excepción si una prioridad de NSLayoutPriorityRequireden OS X o UILayoutPriorityRequireden iOS se cambia a una prioridad más baja, o si una prioridad más baja se cambia a una prioridad requerida después de agregar las restricciones a una vista. Se permite cambiar de una prioridad opcional a otra prioridad opcional incluso después de que la restricción esté instalada en una vista.

Utilice la prioridad 999 en lugar de 1000. No será absolutamente necesario técnicamente hablando, pero será una prioridad más alta que cualquier otra cosa.

Cyrille
fuente
4
Gracias por su buena respuesta, pero si uso 999 en lugar de 1000, no puedo ocultar mi vista asignando 0 a la restricción de altura.
Le'Kirdok
Ese es otro problema. Puede tener otras limitaciones conflictivas. Si su código ya no falla pero la animación de la vista sigue siendo incorrecta, marque esta pregunta como resuelta; luego abra otro.
Cyrille
agradable, no es un problema usar valores diferentes (999, 900, 500, etc.) :)
user924
7
¿Seriamente? ¿Hay algo que funcione normalmente en el desarrollo de iOS? Se tuvieron que hacer muchas cosas manualmente y / o se tuvieron que implementar toneladas de código de solución debido a errores del sistema o falta de soporte. Perdón por comentario no constructivo.
Luten
6
Parece que esta limitación se ha eliminado en iOS 13. Intenté cambiar una restricción de requireda defaultLowy funcionó. El mismo código usado para fallar en iOS 12.
Steven Vandeweghe
52

Quiero hacer una pequeña adición a la respuesta de Cyrille.

Si está creando una restricción en el código, asegúrese de establecer su prioridad antes de activarla. Por ejemplo:

surveyViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self
                                               attribute:NSLayoutAttributeHeight
                                               relatedBy:NSLayoutRelationEqual
                                                  toItem:self.superview
                                               attribute:NSLayoutAttributeHeight
                                              multiplier:1
                                                constant:0];
surveyViewHeightConstraint.active = YES;
surveyViewHeightConstraint.priority = 999;

Esto resultaría en una excepción en tiempo de ejecución.

No se admite la mutación de una prioridad de requerida a no en una restricción instalada (o viceversa).

El orden correcto es:

surveyViewHeightConstraint.priority = 999;
surveyViewHeightConstraint.active = YES;

Para Swift versión 4+

constraintName.priority = UILayoutPriority(rawValue: 999)
Vipin
fuente
13

Versión rápida:

myContraint.priority = UILayoutPriority(999.0)
Alex
fuente
8

La forma en que siempre hemos manejado esto es no cambiando la constante de restricción, solo la prioridad. Por ejemplo, en su situación, tenga dos restricciones de altura.

 heightConstraintOne (who's height is set in the storyboard at 150, and priority set at 750)
 heightConstraintTwo (who's height is set in the storyboard at 0, and priority set at 250)

si desea ocultar la vista, usted:

heightConstraintTwo.priority = 999;

Asimismo, si desea mostrar la vista:

heightConstraintTwo.priority = 250;
J Andrew McCormick
fuente
5
Genial,
entendido
3

Espero que este escenario ayude a alguien: tenía dos restricciones, que eran opuestas entre sí, quiero decir que no podían satisfacerse al mismo tiempo, en el constructor de interfaces una de ellas tenía una prioridad 1000 y la otra tenía menos de 1000 prioridad. cuando los cambié en el código, tuve este error:

Finalizando la aplicación debido a una excepción no detectada 'NSInternalInconsistencyException', motivo: 'No se admite la mutación de una prioridad de requerida a no en una restricción instalada (o viceversa). Pasó la prioridad 250 y la prioridad existente era 1000 '.

luego los inicialicé en la función viewDidLoad con valores .defaultHigh y .defaultLow y esto solucionó mi problema.


fuente
1

Según el NSLayoutConstraints classinterior UIKit Module

Si el nivel de prioridad de una restricción es menor que UILayoutPriorityRequired, entonces es opcional. Las restricciones de mayor prioridad se cumplen antes que las restricciones de menor prioridad. La satisfacción de la restricción no es todo o nada. Si una restricción 'a == b' es opcional, eso significa que intentaremos minimizar 'abs (ab)'. Esta propiedad solo puede modificarse como parte de la configuración inicial o cuando sea opcional. Después de que se ha agregado una restricción a una vista, se lanzará una excepción si la prioridad se cambia de / a NSLayoutPriorityRequired.

Ejemplo: - UIButtonrestricciones con varias prioridades -

 func setConstraints() {
        buttonMessage.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint(item: buttonMessage, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: -10).isActive = true

        let leading = NSLayoutConstraint(item: buttonMessage, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 10)

        leading.isActive = true


        let widthConstraint = NSLayoutConstraint(item: buttonMessage, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100)

        let heightConstraint = NSLayoutConstraint(item: buttonMessage, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)


        let trailingToSuperView = NSLayoutConstraint(item: buttonMessage, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)

        trailingToSuperView.priority = 999
        trailingToSuperView.isActive = true

        //leading.isActive = false//uncomment & check it will align to right of the View

        buttonMessage.addConstraints([widthConstraint,heightConstraint])

         }  
Jack
fuente
1
No, nunca debe crear restricciones en viewDidLayoutSubviews. Ese método se puede llamar varias veces, lo que puede bloquear su aplicación si crea restricciones duplicadas allí.
mph
0

Estaba enfrentando el mismo problema. Como algunas de las respuestas mencionadas anteriormente en iOS 13, el cambio de prioridad funcionará bien, sin embargo, en iOS 12 provocará un bloqueo.

Pude solucionar este problema creando IBOutlet NSLayoutConstraint para esa restricción específica en Storyboard conservando su prioridad a 1000, a continuación se muestra el código para corregirlo.

if (Condition) {
    surveyViewHeightConstraint.constant = 0;
    surveyViewHeightConstraint.isActive = false;
} else if (index == 1) {
    surveyViewHeightConstraint.constant = 163;
    surveyViewHeightConstraint.isActive = True;
}

¡¡¡Espero que esto ayude!!! Salud

Venkat057
fuente
-3

Ahora puede, simplemente tome la conexión IBOutlet para sus restricciones, luego use este código.

self.webviewHeight.priority = UILayoutPriority(rawValue: 1000)
Kishore Kumar
fuente