Когда я могу активировать/деактивировать ограничения макета?
Я установил несколько наборов ограничений в 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
, что границы обзора завершены.
- Можно ли активировать/деактивировать ограничения из viewDidLoad?
Ответ: Нет. Причина, объясненная выше.