«Auto Layout все еще требуется после выполнения -layoutSubviews» с подклассом UITableViewCell

115

Используя XCode 4.5 и iOS 6, я разрабатываю приложение с простым табличным представлением с настраиваемыми ячейками. Я делал это сто раз в iOS 5 и ниже, но по какой-то причине новая система autoLayout доставляет мне много проблем.

Я настраиваю табличное представление и ячейку прототипа в IB, добавляю подвиды и подключаю их как IBOutlets, затем настраиваю делегата и источник данных. Однако теперь всякий раз, когда извлекается первая ячейка cellForRowAtIndexPath, я получаю следующую ошибку:

*** Ошибка утверждения в - [ShopCell layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2372/UIView.m:5776

*** Завершение работы приложения из-за неперехваченного исключения «NSInternalInconsistencyException», причина: «Auto Layout все еще требуется после выполнения -layoutSubviews. Реализация ShopCell -layoutSubviews должна вызывать super.

Я не реализовал метод -layoutSubviews в своей подклассовой ячейке (ShopCell), и даже когда я пытаюсь это сделать и добавить супер-вызов, поскольку он предполагает, я все равно получаю ту же ошибку. Если я удалю подпредставления из ячейки в IB и изменю их на стандартный UITableViewCell, все будет работать так, как ожидалось, хотя, конечно, в моих ячейках не останется данных.

Я почти уверен, что мне не хватает чего-то простого, но я не могу найти никакой документации или руководств, чтобы предположить, что я сделал не так. Любая помощь будет оценена.

Изменить: просто попытался изменить его на UITableViewCell в IB и оставить все подпредставления на месте, все та же ошибка.

Майк Мэйо
источник
lldb [[UIWindow keyWindow] _autoLayoutTrace]Если используется автоматический макет, попробуйте в области отладчика.
A-Live
3
Вы используете UIView для настраиваемой ячейки вместо UITableViewCell? У меня такая же проблема. У меня был UIView для пользовательской ячейки, и я добавлял к ней подвиды. Изменился на UITableViewCell, и все заработало.
Привет, Майк, как ты определяешь торговые точки? Являются ли они свойствами в вашем файле реализации в расширении класса?
kocodude 04
@ A-Live Всякий раз, когда я пытаюсь использовать этот метод, я получаю сообщение об ошибке в отладчике .... этот метод еще действителен? Изменить: Неважно, это строчная буква l в автопластике.
borrrden
снимите флажок autoLayout в инспекторе, затем очистите и запустите. это будет угрюмо работать.
Нико

Ответы:

57

Я столкнулся с той же проблемой при добавлении ограничений в код вручную. В коде я делал следующее:

{
    [self setTranslatesAutoresizingMaskIntoConstraints:YES];
    [self addSubview:someView];
    [self addSubview:someOtherView];
    [self addConstraint:...];
}

гипотеза

Насколько я могу судить, проблема в том, что при отключении translatesAutoresizingMaskIntoConstraintsUITableViewCell начинает использовать Auto Layout и, естественно, дает сбой, потому что базовая реализация layoutSublayersForLayerне вызывает super. Кто-то с Хоппером или другим инструментом может это подтвердить. Поскольку вы используете IB, вам, вероятно, интересно, почему это проблема ... и это потому, что использование IB автоматически отключает translatesAutoresizingMaskIntoConstraintsпредставления, к которым он добавляет ограничения (вместо них автоматически добавляются ограничения ширины и высоты).

Решение

Моим решением было переместить все в папку contentView.

{
   [self.contentView addSubview:someView];
   [self.contentView addSubview:someOtherView];
   [self.contentView addConstraint:...];
}

Я не уверен на 100%, будет ли это работать в Interface Builder, но если вы вытолкнете все из своей ячейки (при условии, что у вас есть что-то прямо на ней), это должно сработать. Надеюсь, это вам поможет!

Malaxeur
источник
4
Мне также нужно было добавить subview.translatesAutoresizingMaskIntoConstraints = NO'в каждое подпредставление, которое я добавлял в contentView.
Джей Пейер,
5
Это сработало для меня. Кроме того, убедитесь, что вы не звоните в службу self.contentView.translatesAutoresizingMaskIntoConstraints = NOподдержки UITableViewCell.
Маурицио
53

По-видимому, реализация layoutSubviews UITableViewCell не вызывает super, что является проблемой с автоматической компоновкой. Мне было бы интересно посмотреть, исправит ли что-то удаление приведенной ниже категории в проекты. Это помогло в тестовом проекте.

#import <objc/runtime.h>
#import <objc/message.h>

@implementation UITableViewCell (FixUITableViewCellAutolayoutIHope)

+ (void)load
{
    Method existing = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method new = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existing, new);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

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

Примечание. Похоже, что эта ошибка исправлена ​​в iOS7; Мне удалось удалить этот код или, по крайней мере, добавить проверку времени выполнения, чтобы она выполнялась только при работе на iOS6.

Карл Линдберг
источник
Странно то, что для меня он отлично работает с простым UITableViewCell, но не для подкласса ...
borrrden
Можно подумать, но все, что у меня есть, это метод инициализации, ничего больше> <. Я никогда в жизни не переопределял layoutSubviews, ха-ха. Я думаю, проблема в том, что пользовательское представление, которое UITableViewCell использует в качестве корневого представления, не может использовать автоматический макет, потому что оно переопределяет layoutSubviews (поэтому, когда вы пытаетесь добавить ограничения в корневое представление, это не сработает)
borrrden
6
Мне пришлось создать такую ​​категорию по UITableViewтой же причине (iOS 6.1 b1)
Джошуа Дж. Маккиннон
5
Есть ли аналогичное исправление для TableHeaderView, поскольку проблема все еще существует в ios 7?
Softlion
1
Это прекрасно работает. Я столкнулся с этой проблемой, когда попытался центрировать подпредставление UIVIew в UITableView. Даже в iOS 7 такое утверждение происходит. Но этого не происходит в iOS 8, поэтому они, должно быть, устранили ошибку.
Jordan H
33

У меня была такая же ошибка несколько месяцев. Но я нашел, в чем проблема.

Когда я создаю файл IB, UIViewон уже добавлен. Если вы используете это представление, приложение не вылетает при отключении автоматического макета (но есть и другие проблемы). При использовании автоматической компоновки, вы должны выбрать правильный вид в библиотеке объектов: UITableViewCell.

На самом деле, вы должны всегда использовать этот пункт , потому что все подвиды добавляют к contentViewиз UITableViewCell.

Вот и все. Все будет хорошо.

Arnaud
источник
Это не должен быть принятым ответом, потому что вопрос не в реализации с использованием IB, а потому, что эта проблема может возникнуть, когда вы не используете IB. Если вы делаете свои взгляды программно, ответ @ PhilLoden более жизнеспособен.
Эрик
Я не понял ответа. может кто-нибудь объяснить более четко? спасибо
hasan
Думаю, я имею на это право. Достаточно ли проверить класс att. в инспекторе удостоверений в построителе интерфейсов? или добавленное было другого типа и класса att. был обновлен позже? это тоже вызывает проблему?
hasan
@ hasan83 Вы действительно можете вернуть ячейку. UITableViewCell - это в основном UIView с идентификатором повторного использования.
Арно
17

У меня была UITableViewHeaderFooterViewтакая же проблема с custom + xib.

Я видел здесь несколько ответов, но обнаружил, какая реализация -layoutSubviewsв моем пользовательском классе представления нижнего колонтитула устраняет проблему:

-(void)layoutSubviews
{
    [super layoutSubviews];
    [self layoutIfNeeded]; // this line is key
}
Sound Blaster
источник
1
Учтите , что это может привести к бесконечной петле и , наконец , EXC_BAD_ACCESS KERN_PROTECTION_FAILURE
MBI
15

Я видел это в результате изменения ограничений в моей реализации layoutSubviews. Перемещение вызова к super с начала до конца метода устранило проблему.

Фил Лоден
источник
Это сработало для меня. У меня есть собственный UICollectionViewCell, который я форматирую в layoutSubviews. Кто-нибудь знает, почему это решение работает?
STANGMMX
@STANGMMX, ответ A'sa Dickens объясняет, почему.
Фабио Оливейра
15

Была такая же проблема в iOS 7 (iOS 8 вроде бы исправил). Решением для меня было вызвать [self.view layoutIfNeeded]в конце моего viewDidLayoutSubviewsметода.

Keller
источник
Спасибо. Мне помогает, вчера (на iOS 7) столкнулся с этой проблемой. это помогает для iOS 7.
Александр
@MaciejSwic см. Мой ответ вверху.
Sound Blaster
Это сработало для меня! Использование iOS 7.1 с Swift. Я удалял и добавлял ограничение для viewDidLayoutSubviews. Я удалил супер-вызов, но он все еще не работал, но это решение помогло! дайте этому динозавру лист! :)
jomafer 05
У меня тоже работало на iOS 7.1!
fdlr
14

Я была такая же проблема. Проблема заключалась в том, как я создавал ячейку Xib. Я создал Xib, как обычно, и просто изменил тип «UIView» по умолчанию на свой собственный класс UITableViewCell. Правильный способ - сначала удалить представление по умолчанию, а затем перетащить объект ячейки табличного представления на xib. Подробнее здесь: http://allplayers.github.io/blog/2012/11/18/Custom-UITableViewCell-With-NIB/

Трунал Бхансе
источник
1
Идеальное понимание! Мне потребовалось время, чтобы понять это, особенно потому, что мои приложения не вылетели бы, если бы я использовал UIView с отключенным AutoLayout.
Guilherme
Супер! также @Arnaud respone ниже
Лаббо
7

Я решил проблему, отключив «Автоформат» для всех подпредставлений моей пользовательской ячейки табличного представления.

В xib для настраиваемой ячейки выберите подпредставление и снимите флажок File Inspector> Interface Builder Document> Use Autolayout

Johno
источник
4
Я сделал то же самое. Хотя это не совсем решение, если вы хотите использовать
автопрокладку
7

У меня была подобная проблема не на себе, UITableViewCellа скорее на UITableViewсебе. Поскольку это первый результат в Google, я выложу его здесь. Оказалось, что это viewForHeaderInSectionбыла проблема. Я создал UITableViewHeaderFooterViewи набор translatesAutoresizingMaskIntoConstraintsдля NO. А теперь самое интересное:

IOS 7:

// don't do this on iOS 7
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

Если я сделаю это, приложение вылетит с

Auto Layout по-прежнему требуется после выполнения -layoutSubviews. Реализация -layoutSubviews в UITableView должна вызывать super.

Хорошо, я думал, вы не можете использовать автоматический макет в заголовке представления таблицы и только в подпредставлениях. Но это не вся правда, как вы увидите позже. Подводя итог: не отключайте маску автоматического изменения размера для заголовка на iOS 7. В противном случае все работает нормально.

iOS 8:

// you have to do this, otherwise you get an auto layout error
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

Если бы я не использовал это, я бы получил следующий результат:

Невозможно одновременно удовлетворить ограничения.

Для iOS 8 необходимо отключить автоматическое изменение размера маски для заголовка.

Не знаю, почему это так, но похоже, что Apple исправила некоторые вещи в iOS 8, и автоматическая компоновка работает по-разному в iOS 7 и iOS 8.

тестирование
источник
Приятель, ты просто спас мне день!
Marcin Małysz
5

Как уже было сказано выше, когда вы создаете представление для использования в UITableView, вы должны удалить представление, созданное по умолчанию, и перетащить UITableViewCell или UITableViewHeaderFooterView в качестве корневого представления. Однако есть способ исправить XIB, если вы пропустили эту часть. Вы должны открыть файл XIB в текстовом редакторе и в корневом теге и его прямой потомок добавить / изменить атрибут translatesAutoresizingMaskIntoConstraintsк YES, например ,

<view contentMode="scaleToFill" horizontalHuggingPriority="1000" id="1" translatesAutoresizingMaskIntoConstraints="YES" customClass="MXWCollapsibleTableHeaderView">

Максимилиан Вояковски
источник
2

Я сталкиваюсь с этим, и кажется, что это связано с подклассами UITableViewCell как с ячейками прототипа, в которые специально добавлены другие настраиваемые подклассы UIView. Я подчеркиваю здесь «обычай», потому что я добился успеха с ячейками, у которых есть только дочерние элементы UIKit, но он падает при попытке создать ограничения для представлений, которые я создал на заказ, что приводит к ошибке, указанной в вопросе авторов.

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

Будем надеяться, что Apple уберет этот беспорядок.

Ли Проберт
источник
2

Добавьте свои подпредставления в contentView ячейки вместо самой ячейки. Так что вместо:

[self addSubview:someView];

вы должны использовать

[self.contentView addSubview:someView];

Кристиан Абергер
источник
1

Я столкнулся с этим, потому что изначально я добавил UIView вместо UITableViewCell в файл xib.

mjmdavis
источник
1

Я устранил эту ошибку, отсоединив backgroundViewконнектор от фона, UIImageViewа accessoryViewконнектор - от UIButtonнастроек. Я подозреваю, что они не предназначались для использования так, как я их использовал.

Оуэн Годфри
источник
1

Сегодня я впервые столкнулся с этой проблемой. До сих пор у меня был некоторый опыт использования прототипов подклассов UITableViewCell, но я никогда не сталкивался с этой проблемой. Что отличало ячейку, с которой я работал, так это то, что у меня был IBOutlet для -backgroundView, который я использовал для раскрашивания ячейки. Я обнаружил, что если я создал новое свойство и все же добавил новый UIView, который растягивал всю ячейку, это утверждение исчезло. Чтобы убедиться, что это была причина, я вернулся к прикреплению этого представления к выходу backgroundView, и утверждение снова появилось. Пока нет других проблем с использованием AutoLayout в подклассе прототипа UITableViewCell, поскольку я внес это изменение.

Эрик Шрамм
источник
1

У меня не было подходящего решения для этой проблемы, но вы можете исправить это, используя фреймы и не устанавливая для свойства translatesAutoresizingMaskIntoConstraints значение Нет (по умолчанию это да, поэтому не устанавливайте его)

CGRect headerViewFrame = CGRectMake(0,0,320,60); //Frame for your header/footer view
UIView *tableHeaderView = [[UIView alloc] initWithFrame:headerViewFrame];
tableHeaderView.translatesAutoresizingMaskIntoConstraints = Yes; //Or don't set it at all
[self.tableView setTableHeaderView:tableHeaderView];
Sanjana
источник
0

Я испытал то же самое. Оказалось, что если вы программно добавляете подпредставление из вашего ShopCell .xib / раскадровки, которое использует автоматический макет в качестве подпредставления для другого представления, это исключение может быть сгенерировано, в зависимости от того, как настроены ваши ограничения. Я предполагаю, что ограничения, созданные в IB, - это то, что создает проблемы при программном добавлении представления в качестве подпредставления, поскольку оно затем поддерживает ограничения из viewA -> viewB, в то время как вы можете добавить viewB как подвид viewC. Вы поняли (это предложение даже меня сбивает с толку)?

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

Каспер Мунк
источник
0

В некоторых ситуациях это легко решает проблему макета (в зависимости от вашего макета). Внутри подкласса UITableView, либо в awakeFromNib, либо в init, установите маску автоматического изменения размера:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

По умолчанию установлено значение UIViewAutoresizingNone

Арье Литовски
источник
Это решило проблему, с которой я столкнулся. Я использую автоматическую компоновку в ячейке таблицы в сочетании с [tableViewCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].heightполучением высоты, которую затем использую heightForRowAtIndexPath.
NathanAldenSr
0

В моем случае,

Указанный UIImageView для автоматического макета для UITableView назначается backgroundView UITableView.

self.tableView.backgroundView = self.tableBackgroundImageView;

Итак, я удалил UIImageView для backgroundView из UIView (корневое представление) и сбросил (удалил) все ссылки автоматического макета на этот UIImageView. Я разместил этот UIImageView для фона снаружи от UIView (корневого представления). А затем назначьте backgroundView UITableView в коде.

Потом исправил.

Конан Ким
источник
0

Я нашел решение.

В моем случае я создал представление ячейки в раскадровке (с включенной автоматической компоновкой) и определил пользовательский интерфейс UITableViewCell в моем ViewController.m, мне нужно переместить интерфейс в ViewController.h.

Ним Титипариват
источник
0

Я столкнулся с той же проблемой, когда использую раскадровку для создания пользовательского UITableViewCell. К счастью, я нашел проблему, потому что я выводил accessoryView ([UITableViewCell setAccessoryView:]) в UIButton, который я добавил в ячейку.

Так произошло в моем проекте при запуске на iOS6.

Решение

Я освобождаю розетку между accessoryView и моей кнопкой, содержащей настраиваемую ячейку.

Предложение

Вы не должны использовать собственные элементы UITableViewCell и изменять их.

Ванцян Цзи
источник
0

Эта проблема может быть вызвана тем, что вы забыли позвонить [super viewDidAppear:]внутрь viewDidAppear, но я уверен, что это не единственная причина.

michaelsnowden
источник
0

У меня была точно такая же проблема. Вот проблема с моим проектом:
Когда я работал на Interface Builder для создания пользовательских UITableViewCell, я перетащил View вместо Table View Cell из панели коллекции объекта в Xcode
как ячейки пользовательских таблиц.
Если вы находитесь в такой же ситуации, вот решение:
удалите представление в построителе интерфейса, убедитесь, что вы перетащили ячейку табличного представления из панели коллекции объектов и повторите создание настраиваемого представления ячейки таблицы. Вы можете скопировать объекты в старом представлении и вставить их на холст для новой ячейки табличного представления.

us_david
источник
0

У меня была очень похожая проблема с представлением нижнего колонтитула таблицы, которое я устанавливал в Xcode 6, iOS 7+. Решение было в формате файла пера. Очевидно, он застрял в формате Xcode 4 или что-то в этом роде. Изменение настроек файла на «открывается в: Xcode 6.0» (или по умолчанию, если на то пошло) мгновенно исправило это. Решение нашел случайно: это сводило меня с ума, поэтому я удалил весь файл и создал заново, очевидно, с настройками по умолчанию. Я понятия не имею, почему простое редактирование файла в последней версии Xcode не преобразовало его в формат Xcode 5+, как это обычно бывает.

е

user3099609
источник
0

У меня была такая же проблема. Я зашел в свой DetailViewController и переименовал идентификатор в UIView. Ранее он был на UITableView. Это устранило проблему. Эта проблема не обязательно должна быть в вашем DetailViewController. Может быть в любом другом. Попробуйте переименовать его в уважаемый идентификатор.

RandomDude
источник
0

У меня была аналогичная проблема со статическими ячейками табличного представления в IB. В одной из ячеек было вложенное представление, в котором был класс, ошибочно измененный на подкласс UITextfield. Никаких предупреждений / ошибок компилятор не выдавал. Но во время выполнения система не смогла загрузить контроллер представления из-за вышеупомянутого сбоя.

Янник Винтерс
источник
0

Решение: изменить ограничения перед вызовом супер-макета

- (void)layoutSubviews
{

    [self _updateConstraints];

    [super layoutSubviews];
}
abuharsky
источник
0

Я изменил ответ Карла Линдберга, чтобы переопределить UITableViewего, и он начал работать для меня:

UITableView + AutoLayoutFix.h

@interface UITableView (AutoLayoutFix)
@end

UITableView + AutoLayoutFix.m

#import <objc/runtime.h>

@implementation UITableView (AutoLayoutFix)

+ (void)load
{
    Method existingMethod = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method newMethod = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existingMethod, newMethod);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

Затем MyViewController.mя просто импортировал категорию:

#import "UITableView+AutoLayoutFix.h"
пахарь
источник
0

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

Альфи
источник