Проблема с ограничениями автоматического макета на iOS7 в UITableViewCell


104

Я программно использую ограничения автоматической компоновки для компоновки своих пользовательских ячеек UITableView, и я правильно определяю размеры ячеек в tableView:heightForRowAtIndexPath:

Это работает просто отлично на iOS6 и делает взгляд штраф в iOS7 , а также

НО, когда я запускаю приложение на iOS7, в консоли я вижу такое сообщение:

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2013-10-02 09:56:44.847 Vente-Exclusive[76306:a0b] 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:0xac4c5f0 V:|-(15)-[UIImageView:0xac47f50]   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
        "<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>",
        "<NSLayoutConstraint:0xac43680 V:[UIView:0xac4d0f0(1)]>",
        "<NSLayoutConstraint:0xac436b0 V:[UIView:0xac4d0f0]-(0)-|   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>

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

"<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"

и я не могу установить translatesAutoresizingMaskIntoConstraintsсвойство contentViewNO => это испортит всю ячейку.

44 - это высота ячейки по умолчанию, но я определил свои собственные высоты в делегате табличного представления, так почему же элемент contentView имеет это ограничение? Что могло вызвать это?

В iOS6 этого не происходит, и все выглядит отлично как на iOS6, так и на iOS7.

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

Чтобы указать, как я это делаю, при инициализации ячейки:

  • Я создаю все свои ярлыки, кнопки и т. Д.
  • Я установил их translatesAutoresizingMaskIntoConstraintsсвойство в NO
  • Я добавляю их как подвиды contentViewячейки
  • Я добавляю ограничения на contentView

Мне также очень интересно понять, почему это происходит только на iOS7.


Какая у вас настроенная высота ячейки?
Майк Поллард

моя нестандартная высота ячейки установлена ​​на 90
Alexis

Вчера у коллеги была такая же проблема, но с шириной по умолчанию 320 (для приложения для iPad).
jrturton 03

Здесь есть образец проекта, демонстрирующий ту же проблему: github.com/Alex311/TableCellWithAutoLayout Вот некоторые из моих наблюдений по этому
поводу

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

Ответы:


132

У меня тоже была эта проблема. Похоже, что фрейм contentView не обновляется до тех пор, пока не layoutSubviewsвызывается, однако фрейм ячейки обновляется раньше, оставляя фрейм contentView установленным {0, 0, 320, 44}на время оценки ограничений.

Посмотрев на contentView более подробно, кажется, что autoresizingMasks больше не устанавливаются.

Установка autoresizingMask перед ограничением ваших представлений может решить эту проблему:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
    if (self)
    {
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
        [self loadViews];
        [self constrainViews];
    }
    return self;
}

Спасибо, мужик. Что могло произойти при использовании стилей редактирования?
Alexis

11
@ L14M333 См. Код, который я разместил в этом комментарии здесь - в основном, вы должны просто установить, self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);прежде чем добавлять свои ограничения, что должно решить проблему.
смайлик борг

2
autoresizingMask у меня не работает: начальное ограничение исчезло, но добавлены 3 новых "<NSAutoresizingMaskLayoutConstraint: 0x8b79740 h = - & - v = - & - UITableViewCellContentView: 0x8b44010.height == UITableViewCellScrollView: 0x8b4> a130", "<NSAutoresizingMaskLayoutConstraint: 0x8b7b9f0 h = - & - v = - & - UITableViewCellScrollView: 0x8b4a130.height == LibraryCell: 0x8ac19d0.height>", "<NSAutoresizingMaskLayout&Constraint: 0x8b: & amp; 0x8ac19d0 (0)]> "
катамфетамин

1
У меня тоже работает, я уже час как борюсь! Спасибо! <3
mokagio

2
Избавившись от других проблем с автоматической компоновкой (iOS 7 / iOS 8), я обнаружил, что использование self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);работает так же хорошо, как и использование self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;. Я вставил это updateConstraintsперед добавлением ограничений в свое представление содержимого.
тестирование

35

По-видимому, что-то не так с UITableViewCell и UICollectionViewCell на iOS 7 с использованием iOS 8 SDK.

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

Для статического UITableViewController:

#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }

    //your code goes here

    return cell;
}

#endif
#endif

Поскольку контроллеры представления статических таблиц хрупки и могут быть легко сломаны, если вы реализуете некоторые методы источника данных или deletegate, существуют проверки, которые гарантируют, что этот код будет скомпилирован и запущен только на iOS 7.

Это похоже на стандартный динамический UITableViewController:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"CellID";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }
    //your code goes here       
    return cell;
}

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

Идея одинакова для обоих случаев и для UICollectionViewCell, как прокомментировано в этом потоке: Проблема с автоматическим изменением размеров кадра UICollectionViewCell contentView в ячейке прототипа раскадровки (Xcode 6, iOS 8 SDK) возникает только при работе на iOS 7


1
Это лучший ответ или, по крайней мере, лучшее решение для меня. Моя ячейка (правила автораспределения) отлично работала в iOS8 с XCode6 и отлично работала с iOS7 и 6 с XCode 5. Я обновляю XCode до XCode6, и он больше не работает в iOS6 и 7. Итак, спасибо !!!!
xarly

Это больше не проблема Xcode 6.1. Кроме того, не используйте NSFoundationVersionNumber, см. Nshipster.com/swift-system-version-checking
onmyway133,

1
@ onmyway133: Почему это больше не проблема в Xcode 6.1? У меня все еще есть проблема, даже если я использую Xcode 6.1 и iOS 8.1 SDK. Проблема возникает только в iOS 7. Изменение границ или установка маски автоматического изменения размера решает проблему. Но я использовал подход, изложенный в ответе, получившем наибольшее количество голосов, или в комментариях к нему.
тестирование

Да, это лучший ответ, основанный на моем 3-часовом поиске в Google. Другие подобные «решения» не предоставляют полный список cell.contentView.autoresizingMask. Только этот работает для моего проекта iPad 7.1, созданного в Xcode 6.
Golden Thumb

13

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

В вашем случае UIImageView имеет интервал 15 сверху, а нижний вид имеет интервал 0 снизу. Если вы установите приоритет этих ограничений на 999 (вместо 1000), приложение не выйдет из строя, потому что ограничения не требуются.

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


Ты гений! красивое и чистое решение, спасибо :)
Blacky

9

Я все еще не нашел хорошего решения для раскадровки ... Некоторая информация также здесь: https://github.com/Alex311/TableCellWithAutoLayout/commit/bde387b27e33605eeac3465475d2f2ff9775f163#commitcomment-4633188

Из того, что они там советуют:

self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);

Я вызвал свое решение:

  • щелкните правой кнопкой мыши раскадровку
  • Открыть как -> Исходный код
  • ищите там строку "44"
  • это будет как

.

<tableView hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="44" ...
    <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatMessageCell" id="bCG-aU-ivE" customClass="ChatMessageCell">
        <rect key="frame" x="0.0" y="22" width="320" height="44"/>

.

  • замените rowHeight = "44" на rowHeight = "9999" и height = "44" на height = "9999"
  • щелкните правой кнопкой мыши раскадровку
  • Открыть как -> Интерфейсный разработчик
  • запустите приложение и проверьте результат

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

3

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


Действительно, мои ячейки выше, чем размер по умолчанию, но как это случилось, что размер по умолчанию используется, поскольку делегат tableview реализует tableView: heightForRowAtIndexPath:?
Alexis

Вероятно, ячейки создаются с размером по умолчанию, а затем изменяются до фактического размера.
Вадим Елагин

действительно, он выполняет свою работу, но почему тогда этого не происходит на iOS6?
Alexis

3
@jafar iOS 7 многое изменила в отношении ячеек табличного представления. В iOS 7 теперь есть вид прокрутки (типа UITableViewCellScrollView) между ячейкой табличного представления и contentView; это, вероятно, объясняет разницу между iOS 6 и 7 здесь.
smileyborg

3

Может быть, установите приоритет представления больше 750, а разрешение меньше 1000.

"<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",

2

У меня такая же проблема. Мое решение основано на других выше:

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        // initialize my stuff
        [self layoutSubviews]; // avoid debugger warnings regarding constraint conflicts
    }
    return self;
}

С уважением, Алессандро


2
Из документации Apple:You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.
Рикардо Санчес-Саез

Я хочу начать религию с молитвы вам. Я искал ЧАСЫ, пытаясь исправить эту проблему, и это решает ее (а также устанавливает для contentView.bounds значение CGRectMake (0, 0, 99999, 99999);) - ЭТО ОШИБКА В КОДЕ ЯБЛОК. Итак, я с уважением не согласен с тем, что в документации по яблокам говорится, что эту функцию не следует вызывать. Apple, исправь свое дерьмо.
Райан Копли

0

Я тоже столкнулся с этой проблемой, и ни одно из предложений не помогло. В моем случае у меня была ячейка выбора размера, и она содержала внутри collectionView (каждая ячейка collectionView содержала полноразмерное изображение). Теперь размер ячейки heigh был немного больше (60), чем collectionView (50) и изображения внутри него (50). Из-за того, что представление collectionView имеет выравнивание снизу по ограничению супервизора со значением 10. В этом случае я предупреждал, и единственный способ исправить это - сделать высоту ячейки такой же, как ее collectionView.


0

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


0

Если он работает нормально в iOS8 и получил предупреждение в iOS7, вы можете выполнить поиск в исходном коде раскадровки и найти правильный tableviewCell, а затем добавить атрибут rect после строки tableviewCell. Спасибо Кучумовн .

<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" .../>
                                <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.