Как изменить приоритет ограничений во время выполнения

86

У меня есть представление с динамической высотой, и я пытаюсь изменить этот приоритет высоты представления во время выполнения.

Вот моя часть кода;

if (index == 0) {

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

} else if (index == 1) {

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

}

Я меняю индекс нажатием кнопки. Когда я запускаю этот код, я получаю такую ​​ошибку:

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

В чем моя ошибка?

Ле'Кирдок
источник

Ответы:

173

Как указано в NSLayoutConstraintссылке на класс :

Приоритеты не могут изменяться с необязательных на обязательные или с обязательных на необязательные. Будет создано исключение, если приоритет NSLayoutPriorityRequiredв OS X или UILayoutPriorityRequirediOS будет изменен на более низкий приоритет, или если более низкий приоритет будет изменен на требуемый приоритет после добавления ограничений в представление. Изменение одного необязательного приоритета на другой необязательный приоритет разрешается даже после того, как ограничение установлено для представления.

Используйте приоритет 999 вместо 1000. Технически говоря, это не обязательно будет абсолютно необходимо, но это будет более высокий приоритет, чем что-либо еще.

Кирилл
источник
4
Спасибо за хороший ответ, но если я использую 999 вместо 1000, я не могу скрыть свое представление, присвоив 0 ограничению высоты.
Le'Kirdok 02
Это еще одна проблема. У вас могут быть другие противоречивые ограничения. Если ваш код больше не дает сбоев, но анимация просмотра все еще некорректна, отметьте этот вопрос как решенный; затем откройте еще один.
Cyrille
хороший, использовать разные значения (999, 900, 500 и т. д.) не проблема :)
user924
7
Шутки в сторону? Есть ли что-нибудь нормально работающее в разработке под iOS? Так много вещей приходилось делать вручную и / или приходилось внедрять тонны кода обходного пути из-за системных ошибок или неподдерживаемости. Извините за неконструктивный комментарий.
Лютен
6
Похоже, это ограничение было снято в iOS 13. Я попытался изменить ограничение с requiredна, defaultLowи это сработало. Тот же код, что и на iOS 12.
Стивен Вандевеге,
52

Я хочу сделать небольшое дополнение к ответу Сирилла.

Если вы создаете ограничение в коде, обязательно установите его приоритет, прежде чем делать его активным. Например:

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

Это приведет к исключению во время выполнения.

Изменение приоритета с обязательного на не установленного ограничения (и наоборот) не поддерживается.

Правильный порядок:

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

Для Swift версии 4+

constraintName.priority = UILayoutPriority(rawValue: 999)
Випин
источник
13

Быстрая версия:

myContraint.priority = UILayoutPriority(999.0)
Alex
источник
8

Мы всегда с этим справлялись, не меняя константу ограничения, а только приоритет. Например, в вашей ситуации есть два ограничения по высоте.

 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)

если вы хотите скрыть вид, вы:

heightConstraintTwo.priority = 999;

аналогично, если вы хотите отобразить представление:

heightConstraintTwo.priority = 250;
Дж. Эндрю МакКормик
источник
5
Круто, понял: используйте 999 вместо 1000. Спасибо
brainray
3

Я надеюсь, что этот сценарий кому-то поможет: у меня было два ограничения, они были противоположны друг другу, я имею в виду, что они не могли быть удовлетворены в одно и то же время, в построителе интерфейса у одного из них был приоритет 1000, а у другого приоритет менее 1000. когда я изменил их в коде, у меня была такая ошибка:

Завершение работы приложения из-за неперехваченного исключения «NSInternalInconsistencyException», причина: «Изменение приоритета с требуемого на не установленного ограничения (или наоборот) не поддерживается. Вы передали приоритет 250, а существующий приоритет - 1000 ».

затем я просто инициализировал их в функции viewDidLoad значениями .defaultHigh и .defaultLow, и это устранило мою проблему.


источник
1

Согласно NSLayoutConstraints classвнутри UIKit Module

Если уровень приоритета ограничения меньше UILayoutPriorityRequired, то это необязательно. Ограничения с более высоким приоритетом выполняются перед ограничениями с более низким приоритетом. Удовлетворение ограничений - это еще не все или ничего. Если ограничение 'a == b' является необязательным, это означает, что мы попытаемся минимизировать 'abs (ab)'. Это свойство может быть изменено только при первоначальной настройке или по желанию. После того, как ограничение было добавлено к представлению, будет выброшено исключение, если приоритет изменится с / на NSLayoutPriorityRequired.

Пример: - UIButtonограничения с разными приоритетами -

 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
Нет, вы никогда не должны создавать ограничений в viewDidLayoutSubviews. Этот метод можно вызывать несколько раз, что может привести к сбою вашего приложения, если вы создадите там повторяющиеся ограничения.
миль / ч,
0

Я столкнулся с той же проблемой. Поскольку некоторые из ответов, упомянутых выше в iOS 13, изменение приоритета будет работать нормально, однако в iOS 12 это приведет к сбою.

Мне удалось решить эту проблему, создав IBOutlet NSLayoutConstraint для этого конкретного ограничения в Storyboard, сохранив приоритет до 1000, ниже приведен код для исправления.

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

Надеюсь это поможет!!! Ура

Venkat057
источник
-3

Теперь вы можете, просто выберите соединение IBOutlet для своих ограничений, а затем используйте этот код.

self.webviewHeight.priority = UILayoutPriority(rawValue: 1000)
Кишор Кумар
источник