Come posso modificare la priorità dei vincoli in fase di esecuzione


87

Ho una vista che ha un'altezza dinamica e sto cercando di cambiare questa priorità di altezza della vista in tempo di esecuzione.

Ecco la mia parte di codice;

if (index == 0) {

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

} else if (index == 1) {

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

}

Sto cambiando l'indice con un'azione del pulsante. Quando eseguo questo codice, ricevo questo errore:

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

Qual è il mio errore qui?

Risposte:


174

Come dichiarato nella NSLayoutConstraintclasse di riferimento :

Le priorità non possono cambiare da non richieste a richieste o da obbligatorie a non richieste. Verrà generata un'eccezione se una priorità di NSLayoutPriorityRequiredOS X o UILayoutPriorityRequirediOS viene modificata in una priorità inferiore o se una priorità inferiore viene modificata in una priorità richiesta dopo che i vincoli sono stati aggiunti a una vista. Il passaggio da una priorità facoltativa a un'altra priorità facoltativa è consentito anche dopo aver installato il vincolo su una vista.

Usa priorità 999 invece di 1000. Non sarà assolutamente necessaria tecnicamente parlando, ma sarà una priorità più alta di qualsiasi altra cosa.


4
Grazie per la tua bella risposta, ma se uso 999 invece di 1000, non posso nascondere la mia vista assegnando 0 al vincolo di altezza.
Le'Kirdok

Questo è un altro problema. Potresti avere altri vincoli contrastanti. Se il codice non si arresta più in modo anomalo ma l'animazione della vista è ancora errata, contrassegnare questa domanda come risolta; quindi aprirne un altro.
Cyrille

bello, non è un problema usare valori diversi (999, 900, 500, ecc.) :)
user924

7
Sul serio? C'è qualcosa che funziona normalmente nello sviluppo iOS? Così tante cose dovevano essere fatte manualmente e / o dovevano implementare tonnellate di codice di soluzione alternativa a causa di bug di sistema o mancanza di supporto. Scusate il commento non costruttivo.
Luten

7
Sembra che questa limitazione sia stata rimossa in iOS 13. Ho provato a cambiare un vincolo da requireda defaultLowe ha funzionato. Lo stesso codice utilizzato per arrestarsi in modo anomalo su iOS 12.
Steven Vandeweghe il

53

Voglio fare una piccola aggiunta alla risposta di Cyrille.

Se stai creando un vincolo nel codice, assicurati di impostarne la priorità prima di renderlo attivo. Per esempio:

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

Ciò comporterebbe un'eccezione in fase di esecuzione.

La modifica di una priorità da obbligatoria a non su un vincolo installato (o viceversa) non è supportata.

L'ordine corretto è:

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

Per Swift versione 4+

constraintName.priority = UILayoutPriority(rawValue: 999)

13

Versione Swift:

myContraint.priority = UILayoutPriority(999.0)

8

Il modo in cui abbiamo sempre gestito questo è non cambiando la costante del vincolo, ma solo la priorità. Ad esempio, nella tua situazione hai due vincoli di altezza.

 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)

se vuoi nascondere la vista, tu:

heightConstraintTwo.priority = 999;

allo stesso modo, se vuoi mostrare la vista:

heightConstraintTwo.priority = 250;

5
Bene, capito: usa 999 invece di 1000. Grazie
brainray

3

Spero che questo scenario aiuti qualcuno: avevo due vincoli, che erano opposti tra loro, voglio dire che non potevano essere soddisfatti nello stesso tempo, nel costruttore di interfacce uno di loro aveva 1000 priorità e l'altro aveva meno di 1000 priorità. quando li ho modificati nel codice, ho avuto questo errore:

Chiusura dell'app a causa di un'eccezione non rilevata "NSInternalInconsistencyException", motivo: "La modifica di una priorità da obbligatoria a non su un vincolo installato (o viceversa) non è supportata. Hai superato la priorità 250 e la priorità esistente era 1000. "

quindi li ho appena inizializzati nella funzione viewDidLoad con i valori .defaultHigh e .defaultLow e questo ha risolto il mio problema.


1

Secondo NSLayoutConstraints classdentro UIKit Module

Se il livello di priorità di un vincolo è inferiore a UILayoutPriorityRequired, è facoltativo. I vincoli di priorità più alta vengono soddisfatti prima dei vincoli di priorità più bassa. La soddisfazione dei vincoli non è tutto o niente. Se un vincolo "a == b" è opzionale, significa che tenteremo di ridurre al minimo "abs (ab)". Questa proprietà può essere modificata solo come parte della configurazione iniziale o quando opzionale. Dopo che un vincolo è stato aggiunto a una visualizzazione, verrà generata un'eccezione se la priorità viene modificata da / a NSLayoutPriorityRequired.

Esempio: - UIButtonvincoli con varie priorità -

 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])

         }  

1
No, non dovresti mai creare vincoli in viewDidLayoutSubviews. Questo metodo può essere chiamato più volte, il che può causare il crash della tua app se crei vincoli duplicati lì.
mph

0

Stavo affrontando lo stesso problema. Poiché alcune delle risposte sopra menzionate in iOS 13, la modifica della priorità funzionerà correttamente, tuttavia in iOS 12 porterà a un arresto anomalo.

Sono stato in grado di risolvere questo problema creando IBOutlet NSLayoutConstraint a quel vincolo specifico in Storyboard mantenendo la sua priorità a 1000, di seguito è riportato il codice per la correzione.

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

Spero che sia di aiuto!!! Saluti


-2

Ora puoi, prendi la connessione IBOutlet per i tuoi vincoli, quindi usa questo codice.

self.webviewHeight.priority = UILayoutPriority(rawValue: 1000)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.