Подтвердить что ты не робот

Автоматическая компоновка: что создает ограничения с именем 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>
4b9b3361

Ответ 1

Основываясь на тонне наблюдения, я считаю (но не знаю точно), что ограничения 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 иметь последнее слово.

Ответ 2

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

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

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

Ответ 3

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

view.translatesAutoresizingMaskIntoConstraints = false

Ответ 4

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

В этом случае мы столкнулись с:

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

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

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

Ответ 5

Я получил эту ошибку во всех случаях (не обязательно привязан к 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];
        }
    }
}

Ответ 6

Определенно видеть это на UITableView 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, вам, вероятно, лучше не хватать что-то за кадром, вы можете выложить la этот метод. Мне не повезло с битами самонастройки.

Ответ 7

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

Ответ 8

Я ловлю эту проблему, когда я использую 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)

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

Ответ 9

Я столкнулся с подобной проблемой и решил ее следующим образом.

  • Среда: Swift 5.0, xcode 10.2.1, программная настройка представлений

  • Предупреждающее сообщение: Невозможно одновременно удовлетворить ограничения... 'UIView-Encapsulated-Layout-Width' UIView: 0x0000000000.width == 0 (активно)> ")

  • Код с предупреждением

    override func loadView() {
    
    view = UIView()
    
    /// Adds the subviews to the view and sets their properties and constraints
    setupViews()
    
    }
    
  • Код, который очистил предупреждение

    override func loadView() {
    
    /// Needed to set the frame of the root view to the window frame.
    let window = UIWindow()
    view = UIView(frame: window.frame)
    
    /// Adds the subviews to the view and sets their properties and constraints
    setupViews()
    }
    
  • Примечания к методу loadView(): "Если вы используете Interface Builder для создания ваших представлений и инициализируете контроллер представлений, вы не должны переопределять этот метод. Вы можете переопределить этот метод для создания ваших представлений вручную. Если вы решите сделать это назначьте корневое представление вашей иерархии представлений свойству представления. Созданные вами представления должны быть уникальными экземплярами и не должны использоваться совместно с любым другим объектом контроллера представления. Ваша пользовательская реализация этого метода не должна вызывать super. " - документация Apple

  • Примечания о корневом представлении:

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

    Создайте объект корневого представления. Корневое представление содержит все другие представления, связанные с вашим контроллером представления. Вы обычно определяете рамку для этого представления, чтобы соответствовать размеру окна приложения, которое само должно заполнить экран. Тем не менее, кадр регулируется в зависимости от того, как отображается ваш контроллер представления. Смотрите "View Controller View Resizing".

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

    Создайте дополнительные подпредставления и добавьте их в корневое представление. "- Старая документация Apple?

Ответ 10

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

[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 с вершиной таблицыView.

Ответ 11

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

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

Ответ 12

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

Ответ 13

После того, как я ударился головой, я нашел эту ссылку. В моем случае это происходило в UITableViewHeaderFooterView, когда я использовал insertRows или deleteRows из моего UIVieController. где оценочные значения "selectedSectionHeaderHeight" и "оценочные значения RowHeight" переданы 3 раза... Показанная ошибка:

"<NSLayoutConstraint:0x280648140 H:|-(8)-[UIImageView:0x106e94860]   (active, names: '|':DAT_Air_Vinyl.ExportsAlbumTableViewHeader:0x106e8fb50 )>",
"<NSLayoutConstraint:0x280648230 H:[UIImageView:0x106e94860]-(8)-[DAT_Air_Vinyl.MainLabel:0x106ea5750'The Coral - The Invisible...']   (active)>",
"<NSLayoutConstraint:0x280648410 H:[UIButton:0x106ea5a40]-(8)-|   (active, names: '|':DAT_Air_Vinyl.ExportsAlbumTableViewHeader:0x106e8fb50 )>",
"<NSLayoutConstraint:0x280648460 H:[DAT_Air_Vinyl.MainLabel:0x106ea5750'The Coral - The Invisible...']-(8)-[UIButton:0x106ea5a40]   (active)>",
"<NSLayoutConstraint:0x2806493b0 'UIView-Encapsulated-Layout-Width' DAT_Air_Vinyl.ExportsAlbumTableViewHeader:0x106e8fb50.width == 0   (active)>"

Как указано в ссылке:

" Когда вы делаете insertRows или deleteRows с определенными типами анимации, UIKit будет анимировать высоту строки от 0 до полной высоты или назад. На 0-м конце этой анимации невозможно решить уравнения макета, если для всей вертикальной оси задан приоритет = 1000. Но понизьте только одно ограничение до 999 - скажем, нижнее пространство для поля superview - и все в порядке; контент будет просто выпадать из-за пределов ячеек ".

Решением было установить 999 (или ниже 1000) главный приоритет UIImageView.