Когда я могу активировать/деактивировать ограничения макета?

Я установил несколько наборов ограничений в IB, и я хотел бы программно переключаться между ними в зависимости от некоторого состояния. Там есть сборка constraintsA, все из которых отмечены как установленные от IB, и коллекцию розетки constraintsB, все из которых удалены в IB.

Я могу программно переключаться между двумя наборами так:

NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)

Но... Я не могу понять, когда это сделать. Похоже, я должен был сделать это один раз в viewDidLoad, но я не могу заставить это работать. Я попытался позвонить view.updateConstraints() и view.layoutSubviews() после установки ограничений, но безрезультатно.

Я обнаружил, что если я установлю ограничения в viewDidLayoutSubviews, все будет работать так, как ожидалось. Наверное, мне хотелось бы узнать две вещи...

  • Почему я получаю такое поведение?
  • Можно ли активировать/деактивировать ограничения из viewDidLoad?

Ответ 1

Я активирую и деактивирую NSLayoutConstraints в viewDidLoad, и у меня нет никаких проблем с ним. Так оно и работает. Должна быть разница в настройке между вашим приложением и моим: -)

Я просто опишу свою установку - возможно, это может дать вам преимущество:

  • Я установил @IBOutlets для всех ограничений, которые мне нужно активировать/деактивировать.
  • В ViewController я сохраняю ограничения в свойствах класса, которые не являются слабыми. Причина этого заключается в том, что я обнаружил, что после деактивации ограничения я не смог его активировать - это было нуль. Таким образом, он удаляется при деактивации.
  • Я не использую NSLayoutConstraint.deactivate/activate, как вы, я использую constraint.active = YES/NO.
  • После установки ограничений я вызываю view.layoutIfNeeded().

Ответ 2

override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}

Ответ 3

Возможно, вы могли бы проверить @properties, заменить weak на strong.

Иногда это потому, что active = NO установить self.yourContraint = nil, чтобы вы не могли использовать self.yourContraint снова.

Ответ 4

Я считаю, что проблема, с которой вы столкнулись, связана с тем, что ограничения не добавляются к их представлениям до тех пор, пока не будет вызвана функция AFTER viewDidLoad(). У вас есть несколько вариантов:

A) Вы можете связать свои ограничения макета с IBOutlet и получить к ним доступ в своем коде этими ссылками. Поскольку выходы подключены до начала viewDidLoad(), ограничения должны быть доступны, и вы можете продолжать активировать и деактивировать их там.

B). Если вы хотите использовать функцию UIView constraints() для доступа к различным ограничениям, вы должны ждать, когда viewDidLayoutSubviews() начнется и сделает это там, так как это первая точка после создания контроллер представления из наконечника, который будет иметь любые установленные ограничения. Не забудьте позвонить layoutIfNeeded(), когда закончите. Это имеет тот недостаток, что пропуск макета будет выполняться дважды, если есть какие-либо изменения для применения, и вы должны убедиться, что не существует возможности запуска бесконечного цикла.

Быстрое предупреждение: отключенные ограничения НЕ возвращаются методом constraints() ! Это означает, что если вы отключите ограничение с намерением снова включить его позже, вам нужно будет сохранить ссылку на него.

C) Вы можете забыть о подходе к раскадровке и вместо этого добавить свои ограничения вручную. Поскольку вы делаете это в viewDidLoad(), я предполагаю, что намерение состоит только в том, чтобы сделать это один раз для полного срока службы объекта, а не менять макет на лету, поэтому это должен быть приемлемый метод.

Ответ 5

Вы также можете настроить свойство priority на "включить" и "отключить" их (значение 750 для включения и 250, например, для отключения). По какой-то причине изменение active BOOL не повлияло на мой пользовательский интерфейс. Нет необходимости в layoutIfNeeded, и его можно установить и изменить в viewDidLoad или в любое время после этого.

Ответ 6

Собственное время для деактивации неиспользуемых ограничений:

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    self.myLittleConstraint.active = NO;
}

Имейте в виду, что viewWillLayoutSubviews можно было бы вызвать несколько раз, поэтому никаких тяжелых вычислений здесь нет, хорошо?

Примечание: если вы хотите позже реагировать на некоторые из ограничений, всегда сохраняйте ссылку strong на них.

Ответ 7

Я обнаружил, что вы установили ограничения на нормаль в переопределении - (void)updateConstraints (цель c), с помощью ссылки strong для инициализации использовались активные и неактивные ограничения. А в другом месте цикла просмотра деактивируйте и/или активируйте то, что вам нужно, затем вызовите layoutIfNeeded, у вас не должно быть проблем.

Главное не постоянно использовать переопределение updateConstraints и отделять активацию ограничений, если вы вызываете updateConstraint после первой инициализации и макета. Кажется, это имеет значение после этого, когда в цикле просмотра.

Ответ 8

Когда создается представление, следующие методы жизненного цикла вызывают по порядку:

  • loadView
  • viewDidLoad
  • viewWillAppear
  • viewWillLayoutSubviews
  • viewDidLayoutSubviews
  • viewDidAppear

Теперь на ваши вопросы.

  • Почему я получаю такое поведение?

Ответ. Потому что, когда вы пытаетесь установить ограничения на представления в viewDidLoad, представление не имеет своих границ, поэтому ограничения не могут быть установлены. Это только после viewDidLayoutSubviews, что границы обзора завершены.

  1. Можно ли активировать/деактивировать ограничения из viewDidLoad?

Ответ: Нет. Причина, объясненная выше.