it-roy-ru.com

Авто-макет: что создает ограничения с именем UIView-Encapsulated-Layout-Width & Height?

Мои ограничения макета в Интерфейсном Разработчике хороши, но исключение происходит во время выполнения благодаря некоторой части структуры, применяющей фиксированные ограничения высоты и ширины, которые я действительно не хочу. Почему они там и как их отключить?

Это последние два ограничения, показанные в зарегистрированном списке:

2014-04-26 09:02:58.687 BBCNews[32058:60b] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0xbf478a0 UIView:0xbf4a3c0.height == 0.28125*UIView:0xbf4a3c0.width>",
    "<NSLayoutConstraint:0xbf47190 UIView:0xbf4a3c0.leading == BNMyNewsCell_landscape:0xbf48b10.leading>",
    "<NSLayoutConstraint:0xbf47160 UIView:0xbf4a3c0.trailing == BNMyNewsCell_landscape:0xbf48b10.trailing>",
    "<NSLayoutConstraint:0xbf47130 BNMyNewsCell_landscape:0xbf48b10.bottom == UIView:0xbf4a3c0.bottom>",
    "<NSLayoutConstraint:0xbf47100 UIView:0xbf4a3c0.top == BNMyNewsCell_landscape:0xbf48b10.top>",
    "<NSLayoutConstraint:0xd4c3c40 'UIView-Encapsulated-Layout-Width' H:[BNMyNewsCell_landscape:0xbf48b10(304)]>",
    "<NSLayoutConstraint:0xd4c38a0 'UIView-Encapsulated-Layout-Height' V:[BNMyNewsCell_landscape:0xbf48b10(290)]>"
}
Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0xbf478a0 UIView:0xbf4a3c0.height == 0.28125*UIView:0xbf4a3c0.width>
61
Reuben Scratton

Основываясь на огромном количестве наблюдений, я считаю (но не могу знать наверняка), что ограничения с именами UIView-Encapsulated-Layout-Width и UIView-Encapsulated-Layout-Height создаются UICollectionView и друзьями и существуют для обеспечения размера, возвращаемого методом делегата sizeForItemAtIndexPath. Я предполагаю, что это сделано для того, чтобы UICollectionViewCell, установленный cellForItemAtIndexPath, заканчивал размер, который, как было сказано, будет.

Который отвечает на мой начальный вопрос здесь. Второй вопрос: почему ограничения были неудовлетворительными? Внутренняя высота ячейки должна быть такой же, как UIView-Encapsulated-Layout-Height. Опять же, я не знаю наверняка, но я подозреваю, что это была ошибка округления (т. Е. Внутренняя высота достигла 200,1 пикселей, UIView-Encapsulated-Layout-Height может быть округлен до 200. Исправление, которое я придумал, состояло в том, чтобы просто снизить приоритет соответствующей ячейки ограничение, позволяющее UIView-Encapsulated-Layout-Height иметь последнее слово.

86
Reuben Scratton

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

Я получал странную ошибку нарушенного ограничения AutoLayout, сопровождаемую ограничением UIView-Encapsulated-Layout-Width, потому что я добавлял tableHeaderView в табличное представление, которое еще не было измерено с помощью AutoLayout. Таким образом, система пыталась применить ограничения моих подвидов заголовков внутри табличного представления с фреймом {0,0,0,0}. Поскольку UITableView любит контролировать ширину своих элементов, его сгенерированное ограничение ширины, UIView-Encapsulated-Layout-Width, было установлено равным нулю, вызывая все виды путаницы с моими элементами заголовка, которые ожидали ширину 320 + pt. 

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

41
Yerk

Я столкнулся с тем же странным ограничением и понятия не имел почему, пока не вспомнил проклятое свойство translatesAutoresizingMaskIntoConstraints. Установка false решила проблему . В фоновом режиме происходит то, что маски с автоматическим изменением размера (старый механизм компоновки для iOS) преобразуются в ограничения. Очень часто вы не хотите эти ограничения и хотите свои собственные. В таких случаях вы должны установить для этого свойства значение false, и все будет в порядке:

view.translatesAutoresizingMaskIntoConstraints = false
5
Zoltán

Мы начали видеть тонны конфликтов макетов в iOS 11, которые включают ссылки на эти ограничения, и они фактически добавляются через флаг translatesAutoresizingMaskIntoConstraints. Кажется, что в iOS 11 намного больше волшебства AutoLayout происходит, когда представление добавляется в иерархию, а не только когда представление размечено (как это, казалось, работало в предыдущих версиях iOS). 

Это тот случай, когда мы столкнулись с:

  • Создайте представление, внутренняя компоновка которого помогает определить размер представлений (например, представление имеет внутренние ограничения, которые включают явное заполнение и т.д.) 
  • *** Добавьте это представление в иерархию.
  • Установите для translatesAutoresizingMaskIntoConstraints значение false через некоторое время, перед передачей макета.

Второй шаг (***) приведет к конфликту, потому что система добавит ограничения нулевого размера к представлению во время добавления представления в иерархию. Мы установили translatesAutoresizingMaskIntoConstraints позже в результате использования инфраструктуры PureLayout, которая автоматически устанавливает этот флаг правильно, когда вы ограничиваете представление ... Тем не менее, в iOS 11 вы должны не забыть отключить translatesAutoresizingMaskIntoConstraints во время построения, прежде чем представление будет добавлено в иерархию. 

Я подозреваю, что Apple подумала, что установка этого флага на YES будет более полезной, чем болезненной. К сожалению, это не так.

4
tyler

Я получил эту ошибку при самых разных обстоятельствах (не обязательно привязанных к UICollectionView и друзьям, как предложено правильным ответом здесь) .. 

Таким образом, мой способ справиться с этим состоял в том, чтобы просто очистить все ограничения и затем построить их снова (только в этот раз я не боюсь столкновения моих ограничений с этими предварительно созданными):

так в коде:

UIView *parentView = [viewInQuestion superview];
[parentView clearConstraintsOfSubview:viewInQuestion];

где clearConstraintsOfSubview - это метод категории в UIView:

- (void)clearConstraintsOfSubview:(UIView *)subview
{
    for (NSLayoutConstraint *constraint in [self constraints]) {
        if ([[constraint firstItem] isEqual:subview] || [[constraint secondItem] isEqual:subview]) {
            [self removeConstraint:constraint];
        }
    }
}
3
abbood

Определенно видя это на UITableView's tableHeaderView. Я смог заставить это работать с настраиваемым представлением заголовка, явно установив ширину, равную ширине tableView после установки tableHeaderView, ТО затем сбросив его после завершения макета. 

Пример кода для iOS 9, который предполагает, что у вас есть UITableView, переданный в ваш метод как tableView, и элемент для его настройки как item

//Create the header view
self.contentDetailHeaderView = MyCustomHeaderView()

//Turn on autolayout
self.contentDetailHeaderView.translatesAutoresizingMaskIntoConstraints = false

//Add the header to the table view
tableView.tableHeaderView = self.contentDetailHeaderView

//Pin the width  
let widthConstraint = NSLayoutConstraint(item: self.contentDetailHeaderView,
    attribute: .Width,
    relatedBy: .Equal,
    toItem: tableView,
    attribute: .Width,
    multiplier: 1,
    constant: 0)

tableView.addConstraint(widthConstraint)

//Do whatever configuration you need to - this is just a convenience method I wrote on my header view.
self.contentDetailHeaderView.setupForItem(item)

//Lay out the configured view
self.contentDetailHeaderView.layoutIfNeeded()

//Reset the table header view, because ¯\_(ツ)_/¯
tableView.tableHeaderView = self.contentDetailHeaderView

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

  • Вы не должны вызывать это из viewDidLayoutSubviews - я мог использовать эту технику, пока tableView имеет соответствующую ширину во время установки. 
  • Вы должны убедиться, что ваш вид заголовка настроен на автоматическое изменение размера. Я сделал это, создав .xib, а затем убедившись, что все элементы были закреплены, чтобы при изменении ширины вида высота обновлялась.
  • Если вы пытаетесь сделать это для viewForHeaderInSection, вам, вероятно, лучше взять что-нибудь за кадром, что вы можете выложить в виде этот прием . Мне не очень повезло с битами самоконтроля. 
2
DesignatedNerd

Я ловлю эту проблему, когда я использую AL создать tableviewHeader

Я инициирую просмотр таблицы, как показано ниже

let table = UITableView.init(frame: .zero, style: .grouped)
// set table constraint ...

тогда я создаю tableviewHeader с AutoLayout. 

"<NSLayoutConstraint:0x600003d7d130 'UIView-Encapsulated-Layout-Width' UIView:0x7fe92bc55260.width == 0 (active)>" 

появляются символические точки останова

После того, как я ссылаюсь на ответ @Yerk ... Я изменяю фрейм, когда запускаю tableView

let rect = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 0)
let table = UITableView.init(frame:rect , style: .grouped)

Кажется, проблема решена

1
柯力乘

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

0
tcarey

У меня была такая же проблема при добавлении ограничений в заголовок табличного представления. Кажется, это происходит при добавлении ограничений с заданными константами, когда границы заголовка были (0,0,0,0). Мне удалось это исправить, добавив только ограничения в методе subviews макета, когда границ заголовка не было (0,0,0,0)

    if self.bounds == CGRect.zero {
        return
    }
0
DairySeeker

У меня была похожая проблема, обнаруженная при тестировании Split View на iPad Pro, и ответ DesignatedNerd сработал, но мне не нужно было так много кода. Вот что я использовал:

[self.tableView.tableHeaderView setTranslatesAutoresizingMaskIntoConstraints:NO];

NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.myTableHeaderView
                                                                   attribute:NSLayoutAttributeWidth
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:self.tableView
                                                                   attribute:NSLayoutAttributeWidth
                                                                  multiplier:1
                                                                    constant:0];
NSLayoutConstraint *yConstraint = [NSLayoutConstraint constraintWithItem:self.myTableHeaderView
                                                               attribute:NSLayoutAttributeTop
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self.tableView
                                                               attribute:NSLayoutAttributeTop
                                                              multiplier:1
                                                                constant:0];


[self.tableView addConstraints:@[widthConstraint, yConstraint]];

Обратите внимание на добавление Y Constraint, который связывает верх tableHeaderView с вершиной tableView.

0
Declan Deckerson

ограничение UIView-Encapsulated-Layout-Height создается со значением, установленным в tableView.estimatedSectionHeaderHeight

0
Maq