Де я повинен встановлювати обмеження авторозкладки при програмному створенні подань


79

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

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


1
Я мав такий самий досвід роботи з updateViewConstraints, тому я перестав намагатися його використовувати. Я налаштовую обмеження у viewDidLoad або в методі updateConstraints власного подання. Сподіваємось, хтось дасть вам остаточну відповідь.
білобатум

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

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перед внесенням змін до деяких обмежень, ви можете отримати виняток під час виконання (збій).


7
Я не впевнений, що це робить щось прагматично, але в документації сказано: "Важливо: Викличте [super updateConstraints] як останній крок у вашій реалізації".
fresidue

Згідно з документами, якщо ви змінюєте подання під час виконання та анулюєте свої обмеження, система негайно видаляє це обмеження та викликає setNeedsUpdateConstraints. Перед виконанням нового макета системний виклик updateConstraints, де ви можете налаштувати недійсний макет. Таким чином, я, мабуть, не встановлював би прапор для цього методу, який може перешкодити системі викликати його.
посмішкаБот

1
@cocoanutmobile Якщо ви використовуєте прапорець BOOL, як пропонується у цій відповіді, це просто для того, щоб деякі ваші обмеження не були додані більше одного разу. Це абсолютно чудова річ. Інша альтернатива - просто зберегти посилання на будь-які обмеження, які ви створюєте, а потім обов’язково видалити (деактивувати) всі ці, перш ніж створювати та активувати будь-які нові. Однак такий підхід призведе до гіршої продуктивності, особливо якщо ваші старі та нові обмеження абсолютно однакові.
smileyborg

2
@cocoanutmobile Крім того, зауважте, що прапор BOOL тут не заважає системі дзвонити -updateConstraintsабо щось інше - вона все одно буде викликана, а ви все одно телефонуєте [super updateConstraints].
smileyborg

1
Як згадував @fresidue, не забудьте зателефонувати superв самому кінці вашої реалізації -updateConstraintsабо -updateViewConstraints. Дивіться цей коментар для отримання додаткової інформації.
smileyborg

4

Це слід робити у ViewDidLoad, відповідно до відео WWDC від Apple та документації.

Не знаю, чому люди рекомендують updateConstraints. Якщо ви зробите це в updateConstraints, ви потрапите на проблеми з NSAutoresizingMaskLayoutConstraint з автоматичною зміною розміру, оскільки ваші подання вже врахували автоматичні маски. Вам доведеться їх видалити в updateConstraints, щоб зробити роботу.

UpdateConstraints мають бути саме для цього, коли вам потрібно їх «оновити», внести зміни тощо від початкового налаштування.


1
Чи можете ви додати тут посилання на відео та документацію, на яку ви посилаєтесь.
Шівам Pokhriyal

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: too:

 override func viewWillLayoutSubviews() {

    if(!wasViewLoaded){
        wasViewLoaded = true

        //update constraint

        //also maybe add a subview            
    }
}

0

Це спрацювало для мене:

Свіфт 4.2

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

// Modify your constraints in here

  ...

}

Хоча, чесно кажучи, я не впевнений, чи варто це робити. Здається, завантаження відбувається трохи повільніше, ніж у viewDidLoad (). Я просто хотів виселити їх з останнього, тому що це стає масовим.


0

Наступним прикладом є передача будь-якого подання іншому класу. створити мій погляд з розкадрування -

Свіфт 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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.