it-roy-ru.com

Что такое NSLayoutConstraint "UIView-Encapsulated-Layout-Height" и как я должен заставить его пересчитать чисто?

У меня есть UITableView, работающий под iOS 8, и я использую автоматическую высоту ячеек из ограничений в раскадровке.

Одна из моих ячеек содержит единственное UITextView, и мне нужно, чтобы он сокращался и расширялся в зависимости от пользовательского ввода - нажмите, чтобы уменьшить/развернуть текст.

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

-(void)collapse:(BOOL)collapse; {

    _collapsed = collapse;

    if(collapse)
            [_collapsedtextHeightConstraint setConstant: kCollapsedHeight]; // 70.0
        else
            [_collapsedtextHeightConstraint setConstant: [self idealCellHeightToShowFullText]];

    [self setNeedsUpdateConstraints];

}

Всякий раз, когда я делаю это, я обертываю это в обновления tableView и вызываю [tableView setNeedsUpdateConstraints]:

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView setNeedsUpdateConstraints];
// I have also tried 
// [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
// with exactly the same results.

[tableView endUpdates];

Когда я делаю это, моя ячейка расширяется (и анимируется при этом), но я получаю предупреждение об ограничениях:

2014-07-31 13:29:51.792 OneFlatEarth[5505:730175] 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:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>",

    "<NSLayoutConstraint:0x7f94dced2260 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']-(15)-|   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced2350 V:|-(6)-[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced6480 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f94de5773a0(91)]>"



 )

Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>

388 - моя расчетная высота, остальные ограничения на UITextView - мои из Xcode/IB.

Последнее меня беспокоит - я предполагаю, что UIView-Encapsulated-Layout-Height - это вычисленная высота ячейки при ее первом отображении - (я установил высоту UITextView равной> = 70.0), однако, тогда это неправильное ограничение отменяет обновленный пользовательский cnstraint.

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

Итак, что такое NSLayoutConstraintUIView-Encapsulated-Layout-Height (я предполагаю, что это расчетная высота для автоматического определения размера ячеек) и как мне заставить его пересчитывать чисто?

225
Rog

Попробуйте снизить приоритет вашего _collapsedtextHeightConstraint до 999. Таким образом, система, предоставившая ограничение UIView-Encapsulated-Layout-Height, всегда имеет приоритет.

Он основан на том, что вы возвращаете в -tableView:heightForRowAtIndexPath:. Убедитесь, что вы вернули правильное значение, и ваши собственные ограничения и сгенерированные должны быть одинаковыми. Более низкий приоритет для вашего собственного ограничения необходим только временно, чтобы предотвратить конфликты, пока анимация свертывания/развертывания находится в полете.

277
Ortwin Gentz

У меня похожий сценарий: табличное представление с одной ячейкой строки, в которой есть несколько строк объектов UILabel. Я использую iOS 8 и autolayout.

Когда я повернулся, я получил неправильную систему, рассчитанную высоту строки (43,5 намного меньше, чем фактическая высота). Это выглядит как:

"<NSLayoutConstraint:0x7bc2b2c0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7bc37f30(43.5)]>"

Это не просто предупреждение. Расположение моей ячейки табличного представления ужасно - весь текст перекрывается одной текстовой строкой.

Меня удивляет, что следующая строка "волшебным образом" решает мою проблему (autolayout ничего не жалует, и я получаю то, что ожидаю на экране):

myTableView.estimatedRowHeight = 2.0; // any number but 2.0 is the smallest one that works

с или без этой строки:

myTableView.rowHeight = UITableViewAutomaticDimension; // by itself this line only doesn't help fix my specific problem
61
Donald

Мне удалось убрать предупреждение, указав priority для одного из значений в ограничении, которое в предупреждающих сообщениях говорится, что оно должно было прерваться (ниже "Will attempt to recover by breaking constraint"). Похоже, что пока я устанавливаю приоритет на что-то большее, чем 49, предупреждение исчезает.

Для меня это означало изменение моего ограничения, в предупреждении говорилось, что оно пытается сломаться:

@"V:|[contentLabel]-[quoteeLabel]|"

чтобы:

@"V:|[email protected][contentLabel]-[quoteeLabel]|"

На самом деле, я могу добавить приоритет любому из элементов этого ограничения, и оно будет работать. Кажется, не имеет значения, какой именно. Мои клетки в конечном итоге нужной высоты, и предупреждение не отображается. Роджер, для вашего примера, попробуйте добавить @500 сразу после ограничения значения высоты 388 (например, [email protected]).

Я не совсем уверен, почему это работает, но я провел небольшое расследование. В перечисление NSLayoutPriority кажется, что уровень приоритета NSLayoutPriorityFittingSizeCompression равен 50. Документация для этого уровня приоритета гласит:

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

документация для указанного сообщения fittingSize гласит:

Минимальный размер представления, который удовлетворяет ограничениям, которые он держит. (Только для чтения)

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

Я не копался в этом, но, похоже, имеет смысл, что это как-то связано с проблемой.

31
Jeff Bowen

Мне удалось устранить эту ошибку, удалив ложное имя cell.layoutIfNeeded(), которое было в моем методе tableViewname cellForRowAtname__.

10
Clifton Labrum

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

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView reloadRowsAtIndexPaths:@[[tableView indexPathForCell:_briefCell]] withRowAnimation:UITableViewRowAnimationNone];

[tableView endUpdates];

UIView-Encapsulated-Layout-Height - это, вероятно, высота таблицы, рассчитанная для ячейки во время начальной загрузки на основе ограничений ячейки в то время.

6
Austin

Другая возможность:

Если вы используете автоматический макет для вычисления высоты ячейки (высота contentView, большую часть времени, как показано ниже), и если у вас есть разделитель uitableview, вам нужно добавить высоту разделителя, чтобы вернуться к высоте ячейки. Как только вы получите правильную высоту, у вас не будет этого предупреждения о автоматическом размещении.

- (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
   [sizingCell setNeedsLayout];
   [sizingCell layoutIfNeeded];
   CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
   return size.height; // should + 1 here if my uitableviewseparatorstyle is not none

}
5
Cullen SUN

Как упомянуто Джесси в комментарии вопроса, это работает для меня:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

К вашему сведению, эта проблема не возникает в iOS 10.

3
Phu Nguyen

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

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

let neededHeight = width / ratio // This is a CGFloat like 133.2353
constraintPictureHeight.constant = neededHeight // Causes constraint error
constraintPictureHeight.constant = ceil(neededHeight) // All good!
2
Che

Потратив несколько часов, почесывая голову с этой ошибкой, я наконец нашел решение, которое сработало для меня. Моя главная проблема заключалась в том, что у меня было зарегистрировано несколько перьев для разных типов ячеек, но одному типу ячеек было разрешено иметь разные размеры (не все экземпляры этой ячейки будут одинакового размера). поэтому проблема возникла, когда табличное представление пыталось удалить из очереди ячейку этого типа, и оказалось, что она имеет другую высоту. Я решил это, установив

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; self.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

что-то вроде

cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; cell.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

Надеюсь это поможет!

1
Alcides Eduardo Zelaya

Изменение размера текстового представления в соответствии с его содержимым и обновление константы ограничения высоты до результирующей высоты позволило мне устранить конфликт ограничения UIView-Encapsulated-Layout-Height, например:

[self.textView sizeToFit];
self.textViewHeightConstraint.constant = self.textView.frame.size.height;
1
Kim André Sand

Я получаю сообщение, подобное этому:

Невозможно одновременно удовлетворить ограничения ...
...
...
...
NSLayoutConstraint: 0x7fe74bdf7e50 'UIView-Encapsulated-Layout-Height' V: [UITableViewCellContentView: 0x7fe75330c5c0 (21.5)]
...
...
Попытка восстановления путем нарушения ограничения NSLayoutConstraint: 0x7fe0f9b200c0 UITableViewCellContentView: 0x7fe0f9b1e090.bottomMargin == UILabel: 0x7fe0f9b1e970.bottom

Я использую пользовательское UITableViewCell с UITableViewAutomaticDimension для высоты. И я также реализовал метод estimatedHeightForRowAtIndex:.

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

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[title]-|" options:0 metrics:nil views:views];

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

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[email protected][title][email protected]|" options:0 metrics:nil views:views];

Однако, что я заметил, так это то, что если я на самом деле просто удаляю приоритет, это тоже работает, и я не получаю журналы нарушения ограничения:

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6-[title]-6-|" options:0 metrics:nil views:views];

Это немного загадка, в чем разница между |-6-[title]-6-| и |-[title-|. Но указание размера не является проблемой для меня и избавляет от логов, и мне не нужно снижать приоритет моих необходимых ограничений.

0
user1687195

TableView получает высоту для ячейки в indexPath от делегата. затем получите ячейку от cellForRowAtIndexPath:

top ([email protected])
    cell
bottom ([email protected])

если cell.contentView.height: 0 // <-> (UIView-Encapsulated-Layout-Height: 0 @ 1000) top (10 @ 1000) конфликтует с (UIView-Encapsulated-Layout-Height: 0 @ 1000),

потому что их приоритеты равны 1000. Нам нужно установить верхний приоритет в соответствии с приоритетом UIView-Encapsulated-Layout-Height.

0
user2962814