Como posso alterar a prioridade das restrições em tempo de execução

86

Eu tenho uma visão que tem altura dinâmica e estou tentando mudar essa prioridade de altura da visão em tempo de execução.

Aqui está minha parte do código;

if (index == 0) {

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

} else if (index == 1) {

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

}

Estou mudando o índice com uma ação de botão. Quando executo este código, recebo este erro:

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

Qual é o meu erro aqui?

Le'Kirdok
fonte

Respostas:

172

Conforme declarado na NSLayoutConstraintreferência da classe :

As prioridades não podem mudar de não obrigatórias para obrigatórias ou de obrigatórias para não obrigatórias. Uma exceção será lançada se uma prioridade de NSLayoutPriorityRequiredno OS X ou UILayoutPriorityRequiredno iOS for alterada para uma prioridade mais baixa, ou se uma prioridade mais baixa for alterada para uma prioridade necessária após as restrições serem adicionadas a uma exibição. A mudança de uma prioridade opcional para outra prioridade opcional é permitida mesmo depois que a restrição é instalada em uma exibição.

Use a prioridade 999 em vez de 1000. Não será absolutamente necessário tecnicamente falando, mas será uma prioridade mais alta do que qualquer outra coisa.

Cyrille
fonte
4
Obrigado pela sua boa resposta, mas se eu usar 999 em vez de 1000, não posso esconder minha visão atribuindo 0 à restrição de altura.
Le'Kirdok
Esse é outro problema. Você pode ter outras restrições conflitantes. Se o seu código não travar mais, mas a animação da visualização ainda estiver incorreta, marque esta questão como resolvida; em seguida, abra outro.
Cyrille
bom, não é um problema usar valores diferentes (999, 900, 500, etc) :)
user924
7
Seriamente? Existe algo funcionando normalmente no desenvolvimento do iOS? Muitas coisas tiveram que ser feitas manualmente e / ou tiveram que implementar toneladas de código de solução alternativa para a causa de bugs do sistema ou falta de suporte. Desculpe pelo comentário não construtivo.
Luten
6
Parece que essa limitação foi removida no iOS 13. Tentei alterar uma restrição de requiredpara defaultLowe funcionou. O mesmo código costumava travar no iOS 12.
Steven Vandeweghe
52

Quero fazer um pequeno acréscimo à resposta de Cyrille.

Se você estiver criando uma restrição no código, certifique-se de definir sua prioridade antes de torná-la ativa. Por exemplo:

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

Isso resultaria em exceção de tempo de execução.

A mutação de uma prioridade de obrigatória para não em uma restrição instalada (ou vice-versa) não é suportada.

A ordem correta é:

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

Para Swift versão 4+

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

Versão Swift:

myContraint.priority = UILayoutPriority(999.0)
Alex
fonte
8

A maneira como sempre tratamos disso é não alterando a constante de restrição, apenas a prioridade. Por exemplo, em sua situação, há duas restrições 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)

se quiser ocultar a visualização, você:

heightConstraintTwo.priority = 999;

da mesma forma, se você quiser mostrar a vista:

heightConstraintTwo.priority = 250;
J Andrew McCormick
fonte
5
Legal, entendi: use 999 em vez de 1000. Obrigado
brainray
3

Espero que este cenário ajude alguém: Eu tinha duas restrições, que eram opostas, quer dizer, não podiam ficar satisfeitas ao mesmo tempo, no construtor de interface um deles tinha 1000 prioridade e outro tinha menos de 1000 prioridade. quando os alterei no código, tive este erro:

Encerrando o aplicativo devido à exceção não detectada 'NSInternalInconsistencyException', motivo: 'A mutação de uma prioridade de necessária para não em uma restrição instalada (ou vice-versa) não é suportada. Você passou a prioridade 250 e a prioridade existente era 1000. '

então, eu apenas os inicializei na função viewDidLoad com os valores .defaultHigh e .defaultLow e isso corrigiu meu problema.


fonte
1

De acordo com NSLayoutConstraints classdentro UIKit Module

Se o nível de prioridade de uma restrição for menor que UILayoutPriorityRequired, ele será opcional. As restrições de prioridade mais alta são atendidas antes das restrições de prioridade mais baixa. A satisfação das restrições não é tudo ou nada. Se uma restrição 'a == b' for opcional, isso significa que tentaremos minimizar 'abs (ab)'. Esta propriedade só pode ser modificada como parte da configuração inicial ou quando opcional. Depois que uma restrição for adicionada a uma visualização, uma exceção será lançada se a prioridade for alterada de / para NSLayoutPriorityRequired.

Exemplo: - UIButtonrestrições com várias 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
fonte
1
Não, você nunca deve criar restrições em viewDidLayoutSubviews. Esse método pode ser chamado várias vezes, o que pode travar seu aplicativo se você criar restrições duplicadas lá.
mph
0

Eu estava enfrentando o mesmo problema. Como algumas das respostas mencionadas acima no iOS 13, alterar a prioridade funcionará bem, mas no iOS 12 isso levará ao travamento.

Consegui corrigir esse problema criando IBOutlet NSLayoutConstraint para essa restrição específica no Storyboard mantendo sua prioridade em 1000, abaixo está o código para correção.

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

Espero que isto ajude!!! Felicidades

Venkat057
fonte
-3

Agora você pode, basta usar a conexão IBOutlet para suas restrições e usar este código.

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