Где мне следует устанавливать ограничения автоматического размещения при программном создании представлений

79

Я вижу разные примеры, когда устанавливаются ограничения. Некоторые устанавливают их в viewDidLoad/ loadView(после добавления подпредставления). Другие устанавливают их в методе updateViewConstraints, который вызывается viewDidAppear.

Когда я пытаюсь установить ограничения, updateViewContraintsможет возникнуть скачок в макете, например небольшая задержка перед появлением представления. Кроме того , если я использую этот метод, я должен очистить существующие ограничения первого т.е. [self.view [removeConstraints:self.view.constraints]?

Гейзенберг
источник
1
У меня был такой же опыт с updateViewConstraints, поэтому я перестал пытаться его использовать. Я настраиваю ограничения в viewDidLoad или в методе updateConstraints пользовательского представления. Надеюсь, кто-то даст вам окончательный ответ.
bilobatum
1
updateViewConstraints: Вы можете переопределить этот метод в подклассе, чтобы добавить ограничения к представлению или его подпредставлениям. (из документации Apple )
тестирование

Ответы:

108

Я установил свои ограничения в viewDidLoad/ loadView(я нацелен на iOS> = 6). updateViewConstraintsполезен для изменения значений ограничений, например, если какое-то ограничение зависит от ориентации экрана (я знаю, это плохая практика), вы можете изменить его constantв этом методе.

Добавление ограничений viewDidLoadпоказано во время сеанса «Введение в автоматический макет для iOS и OS X» (WWDC 2012), начиная с 39:22. Я думаю, что это одна из тех вещей, о которых говорят на лекциях, но не отражаются в документации.

ОБНОВЛЕНИЕ: я заметил упоминание о настройке ограничений в управлении ресурсами в контроллерах представления :

Если вы предпочитаете создавать представления программно, вместо использования раскадровки, вы делаете это путем переопределения loadView метода контроллера представления . Ваша реализация этого метода должна делать следующее:

(...)

3. Если вы используете автоматический макет, назначьте достаточные ограничения для каждого из только что созданных представлений, чтобы контролировать положение и размер ваших представлений . В противном случае, реализовать viewWillLayoutSubviewsи viewDidLayoutSubviewsметоды для настройки каркасов подвидов в иерархии представлений. См. «Изменение размера представлений контроллера представления».

UPDATE 2 : Во время WWDC 2015 Apple , дал новое объяснение о updateConstraintsи updateViewConstraintsрекомендуемое использование:

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

В идеале вся ваша начальная установка ограничений должна происходить внутри Interface Builder.

Или, если вы действительно обнаружите, что вам нужно распределить свои ограничения программно, гораздо лучше какое-нибудь место, например viewDidLoad.

Ограничения обновления на самом деле предназначены только для работы, которую необходимо периодически повторять.

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

Итак, когда вам нужно использовать ограничения обновления?

Что ж, все сводится к производительности.

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

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

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

Арек Холко
источник
2
+1 за это как лучший ответ. Apple предписывает loadView в качестве правильного места для установки начальных ограничений, и для этого не требуется дополнительный флаг BOOL в методе updateConstraints (который кажется хакерским).
awolf
4
Я считаю, что за ограничения должно отвечать представление, а не контроллер представления. Во многих случаях контроллер представления даже не знает, какие элементы в представлении (например, статические метки в ячейке представления таблицы).
dasdom
1
@dasdom Как представление может управлять своим отношением к другим представлениям? Вы должны установить ограничения, как @"|-[button1]-[button2]-|"в ViewController, верно? Или есть другой способ?
Джозеф
@Casper В большинстве случаев у меня есть подкласс UIView, который является представлением контроллера представления. Внутри этого представления есть подпредставления и ограничения для подвидов.
dasdom 03
3
Почему менять ограничения - плохая практика updateViewConstraints?
тестирование
33

Я рекомендую создать BOOL и установить их в -updateConstraintsUIView (или -updateViewConstraintsдля UIViewController).

-[UIView updateConstraints]: (яблочные документы)

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

Оба -updateConstraintsи -updateViewConstraintsмогут вызываться несколько раз в течение жизни представления. (ВызовsetNeedsUpdateConstraints представления вызовет это, например.) В результате вам необходимо убедиться, что вы не создали и не активировали повторяющиеся ограничения - либо с помощью BOOL, чтобы выполнить настройку определенных ограничений только один раз, либо убедившись, что для деактивации / удаления существующих ограничений перед созданием и активацией новых.

Например:

  - (void)updateConstraints {  // for view controllers, use -updateViewConstraints

         if (!_hasLoadedConstraints) {
              _hasLoadedConstraints = YES;
             // create your constraints
         }
         [super updateConstraints];
    }

Приветствую @fresidue в комментариях за указание на то, что в документации Apple рекомендуется позвонить superв качестве последнего шага. Если вы позвоните superперед внесением изменений в некоторые ограничения, вы можете столкнуться с исключительной ситуацией во время выполнения (аварийным завершением).

BooRanger
источник
7
Я не уверен, имеет ли это значение с прагматической точки зрения, но в документации сказано: «Важно: вызовите [super updateConstraints] в качестве последнего шага в вашей реализации».
fresidue 08
Согласно документам, если вы измените представление во время выполнения и аннулируете свои ограничения, система немедленно удалит это ограничение и вызовет setNeedsUpdateConstraints. Перед выполнением нового макета система вызывает updateConstraints, где вы можете настроить недействительный макет. Таким образом, я, вероятно, не стал бы устанавливать флаг для этого метода, который мог бы помешать системе его вызвать.
smileBot 04
1
@cocoanutmobile Если вы используете флаг BOOL, как предложено в этом ответе, это просто для предотвращения добавления некоторых ваших ограничений более одного раза. Это прекрасное занятие. Другой вариант - просто сохранить ссылку на любые создаваемые вами ограничения, а затем обязательно удалить (деактивировать) все из них, прежде чем создавать и активировать любые новые. Однако этот подход приведет к худшей производительности, особенно если ваши старые и новые ограничения совпадают.
smileyborg 06
2
@cocoanutmobile Также обратите внимание, что флаг BOOL здесь не препятствует вызову системы -updateConstraintsили чему-то еще - она ​​все равно будет вызвана, а вы все равно будете звонить [super updateConstraints].
smileyborg 06
1
Как упоминалось в @fresidue, обязательно вызовите superв самом конце вашей реализации -updateConstraintsили -updateViewConstraints. См. Этот комментарий для получения дополнительной информации.
smileyborg
4

Это должно быть сделано в ViewDidLoad, согласно видео WWDC от Apple и документации.

Понятия не имею, почему люди рекомендуют updateConstraints. Если вы это сделаете в updateConstraints, вы столкнетесь с проблемами с NSAutoresizingMaskLayoutConstraint с автоматическим изменением размера, потому что ваши представления уже приняли во внимание автоматические маски. Вам нужно будет удалить их в updateConstraints, чтобы заставить работать.

UpdateConstraints должен быть именно для этого, когда вам нужно «обновить» их, внести изменения и т. Д. Из начальной настройки.

BluelineCoding
источник
1
Не могли бы вы добавить сюда ссылки на видео и документацию, о которых вы говорите.
Шивам Похриял
2

Сделайте это в виду, сделал ли метод подвидов макета

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
}
Комплект
источник
1

У меня есть решение для изменения ограничений до загрузки тех, кто находится в раскадровке. Это решение устраняет любые задержки после загрузки представления.

-(void)updateViewConstraints{

    dispatch_async(dispatch_get_main_queue(), ^{

            //Modify here your Constraint -> Activate the new constraint and deactivate the old one

            self.yourContraintA.active = true;
            self.yourContraintB.active= false;
            //ecc..
           });

    [super updateViewConstraints]; // This must be the last thing that you do here -> if! ->Crash!
}
Давиде
источник
1

Вы можете установить их в viewWillLayoutSubviews: тоже:

 override func viewWillLayoutSubviews() {

    if(!wasViewLoaded){
        wasViewLoaded = true

        //update constraint

        //also maybe add a subview            
    }
}
Андре Саймон
источник
0

Это сработало для меня:

Swift 4.2

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

// Modify your constraints in here

  ...

}

Хотя, честно говоря, не уверен, стоит ли оно того. Кажется, он загружается немного медленнее, чем в viewDidLoad (). Я просто хотел убрать их из последнего, потому что он становится массовым.

Мишель Далл'Агата
источник
0

Следующий пример - передать любое представление другому классу. создать мое представление из раскадровки

Swift 5.0

    override func viewWillAppear(_ animated: Bool) {
        
      super.viewWillAppear(animated) 
        DispatchQueue.main.async {
            self.abcInstance = ABC(frame: self.myView.frame)
          } 
      }

 

Если вы пропустите DispatchQueue.main.async, потребуется время, чтобы обновить ограничения в viewWillAppear. Создайте myView в раскадровке и задайте ограничения, такие же, как ширина и высота экрана, затем попробуйте распечатать фрейм myView. он даст точное значение в DispatchQueue.main.async или в viewDidAppear, но не даст точного значения в viewWillAppear без DispatchQueue.main.async.

Шрикант Пхадке
источник