Подкласс UITableViewHeaderFooterView с автоматической компоновкой и перезагрузкой раздела не будет работать вместе

Я пытаюсь включить автоматическую компоновку в свой подкласс UITableViewHeaderFooterView. Класс довольно простой, всего две метки. Это полный подкласс:

@implementation MBTableDetailStyleFooterView static void MBTableDetailStyleFooterViewCommonSetup(MBTableDetailStyleFooterView *_self) { UILabel *rightLabel = [[UILabel alloc] init]; _self.rightLabel = rightLabel; rightLabel.translatesAutoresizingMaskIntoConstraints = NO; [_self.contentView addSubview:rightLabel]; UILabel *leftLabel = [[UILabel alloc] init]; _self.leftLabel = leftLabel; leftLabel.translatesAutoresizingMaskIntoConstraints = NO; [_self.contentView addSubview:leftLabel]; NSDictionary *views = NSDictionaryOfVariableBindings(rightLabel, leftLabel); NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[leftLabel]-(>=10)-[rightLabel]-10-|" options:0 metrics:nil views:views]; [_self.contentView addConstraints:horizontalConstraints]; // center views vertically in super view NSLayoutConstraint *leftCenterYConstraint = [NSLayoutConstraint constraintWithItem:leftLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]; [_self.contentView addConstraint:leftCenterYConstraint]; NSLayoutConstraint *rightCenterYConstraint = [NSLayoutConstraint constraintWithItem:rightLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]; [_self.contentView addConstraint:rightCenterYConstraint]; // same height for both labels NSLayoutConstraint *sameHeightConstraint = [NSLayoutConstraint constraintWithItem:leftLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:rightLabel attribute:NSLayoutAttributeHeight multiplier:1 constant:0]; [_self.contentView addConstraint:sameHeightConstraint]; } + (BOOL)requiresConstraintBasedLayout { return YES; } - (id)initWithReuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithReuseIdentifier:reuseIdentifier]; MBTableDetailStyleFooterViewCommonSetup(self); return self; } @end 

Этот класс используется как нижний колонтитул в первом разделе в таблицеView с двумя разделами. Первый раздел содержит динамические элементы. Второй раздел имеет только одну строку, которая используется для добавления новых элементов в первый раздел.

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

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; if (indexPath.section == 1) { BOOL sectionNeedsReload = ([self.data count] == 0); // reload section when no data (and therefor no footer) was present before the add [self.data addObject:[NSDate date]]; NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:[self.data count]-1 inSection:0]; if (sectionNeedsReload) { [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic]; } else { [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } [self configureFooter:(MBTableDetailStyleFooterView *)[tableView footerViewForSection:0] forSection:0]; } } - (void)configureFooter:(MBTableDetailStyleFooterView *)footer forSection:(NSInteger)section { footer.leftLabel.text = @"Total"; footer.rightLabel.text = [NSString stringWithFormat:@"%d", [self.data count]]; } - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { MBTableDetailStyleFooterView *footer = nil; if (section == 0 && [self.data count]) { footer = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"Footer"]; [self configureFooter:footer forSection:section]; } return footer; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { CGFloat height = 0; if (section == 0 && [self.data count]) { height = 20.0f; } return height; } 

Ничего особенного. Однако, как только reloadSections:withRowAnimations: вызывается в моем tableView, он выдает исключение, потому что он «не может одновременно удовлетворять ограничениям».

Где-то в таблицеView добавлено преобразование автоматической маскировки маскировки в нижний колонтитул.

 ( "<NSLayoutConstraint:0x718a1f0 H:[UILabel:0x7189130]-(10)-| (Names: '|':_UITableViewHeaderFooterContentView:0x7188df0 )>", "<NSLayoutConstraint:0x7189e30 H:[UILabel:0x71892c0]-(>=10)-[UILabel:0x7189130]>", "<NSLayoutConstraint:0x718a0a0 H:|-(10)-[UILabel:0x71892c0] (Names: '|':_UITableViewHeaderFooterContentView:0x7188df0 )>", "<NSAutoresizingMaskLayoutConstraint:0x7591ab0 h=--& v=--& H:[_UITableViewHeaderFooterContentView:0x7188df0(0)]>" ) 

Когда я заменяю reloadSections:withRowAnimations: с вызовом reloadData не добавляется ограничение авторезистентной маски, и все работает нормально.

Интересно, что исключение говорит мне, что оно пытается сломать ограничение <NSLayoutConstraint:0x7189e30 H:[UILabel:0x71892c0]-(>=10)-[UILabel:0x7189130]>

Но когда я регистрирую ограничения в последующих вызовах configureFooter:forSection: это ограничение все еще существует, но ограничение маски для автоматического изменения размера отсутствует

Ограничения – это именно те, которые я создал.

 ( "<NSLayoutConstraint:0x718a0a0 H:|-(10)-[UILabel:0x71892c0] (Names: '|':_UITableViewHeaderFooterContentView:0x7188df0 )>", "<NSLayoutConstraint:0x7189e30 H:[UILabel:0x71892c0]-(>=10)-[UILabel:0x7189130]>", "<NSLayoutConstraint:0x718a1f0 H:[UILabel:0x7189130]-(10)-| (Names: '|':_UITableViewHeaderFooterContentView:0x7188df0 )>", "<NSLayoutConstraint:0x718a3f0 UILabel:0x71892c0.centerY == _UITableViewHeaderFooterContentView:0x7188df0.centerY>", "<NSLayoutConstraint:0x718a430 UILabel:0x7189130.centerY == _UITableViewHeaderFooterContentView:0x7188df0.centerY>", "<NSLayoutConstraint:0x718a4b0 UILabel:0x71892c0.height == UILabel:0x7189130.height>" ) 

Откуда это ограничение автомасштабирования маски? Куда он идет?

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

Рабочее решение от iOS 9

В подклассе UITableViewHeaderFooterView введите следующий код.

 - (void)setFrame:(CGRect)frame { if (frame.size.width == 0) { return; } [super setFrame:frame]; } 

Объяснение:

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

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

Ограничение, содержащееся в супер-представлении, представляет собой NSAutoresizingMaskLayoutConstraint, который является поддачей, что представление таблицы зависит от фреймов для управления заголовками. Переключение транслятовAutoresizingMaskIntoConstraints на NO в представлении заголовка аффективно нарушает его внешний вид, который является другим.

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

Это решение должно работать хорошо и не должно влиять на производительность прокрутки.

Я наткнулся на эту последнюю неделю.

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

Вещи, которые не сработали для меня.

Предложение состоит в том, чтобы установить estimatedRowHeight рейтинг estimatedRowHeight . Я попытался установить estimatedSectionHeaderHeight , но это не помогло. Установка estimatedSectionFooterHeight SectionFooterHeight создала пустые нижние колонтитулы, где я их не хотел, что было немного странно.

Я также попытался установить translatesAutoresizingMaskIntoConstraints = NO; в представлении нижнего колонтитула заголовка и в виде содержимого. Также не избавились от предупреждения, и один из них полностью нарушил макет.

У меня была аналогичная проблема с только одной дополнительной меткой в ​​contentView. Попробуйте вставить

 static void MBTableDetailStyleFooterViewCommonSetup(MBTableDetailStyleFooterView *_self) { _self.contentView.translatesAutoresizingMaskIntoConstraints = NO [...] } 

на первой строке в вашей функции MBTableDetailStyleFooterViewCommonSetup . Для меня это работает в сочетании с reloadSections:withRowAnimations:

Обновить:

Я также добавил новое ограничение для contentView для использования всей ширины:

 NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:nil views:@{@"contentView" : _self.contentView}]; [_self.contentView.superview addConstraints:horizontalConstraints]; 

Я не вижу, как вы называете translatesAutoresizingMaskIntoConstraints = NO на нижнем колонтитуле. Должны ли вы это делать, когда вы его создаете?

 - (id)initWithReuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithReuseIdentifier:reuseIdentifier]; self.translatesAutoresizingMaskIntoConstraints = NO; MBTableDetailStyleFooterViewCommonSetup(self); return self; } 

У меня была аналогичная проблема, только один UILabel в contentView, и я использовал масонство:

 _titleLabel = ({ UILabel *label = [MLBUIFactory labelWithType:MALabelTypeB]; [self.contentView addSubview:label]; [label mas_makeConstraints:^(MASConstraintMaker */make) { make.centerY.equalTo(self.contentView); make.left.equalTo(self.contentView).offset(8); make.right.equalTo(self.contentView).offset(-8); }]; label; }); 

то я получил предупреждение:

 ( "<MASLayoutConstraint:0x1742b2c60 UILabel:0x124557ee0.left == _UITableViewHeaderFooterContentView:0x12454c640.left + 8>", "<MASLayoutConstraint:0x1742b3080 UILabel:0x124557ee0.right == _UITableViewHeaderFooterContentView:0x12454c640.right - 8>", "<NSLayoutConstraint:0x174280780 _UITableViewHeaderFooterContentView:0x12454c640.width == 0>" ) Will attempt to recover by breaking constraint <MASLayoutConstraint:0x1742b3080 UILabel:0x124557ee0.right == _UITableViewHeaderFooterContentView:0x12454c640.right - 8> 

поэтому я попытался установить self.translatesAutoresizingMaskIntoConstraints = NO; , но это не сработало для меня, поэтому я сосредоточился на предупреждающем сообщении, и я смущен, почему левое ограничение работает, но правильно, когда я увидел _UITableViewHeaderFooterContentView:0x12454c640.width == 0 , я понял, может быть, поэтому правое ограничение – это ограничение на разрыв, а затем я изменил код:

 _titleLabel = ({ UILabel *label = [MLBUIFactory labelWithType:MALabelTypeB]; [self.contentView addSubview:label]; [label mas_makeConstraints:^(MASConstraintMaker */make) { make.centerY.equalTo(self.contentView); make.left.equalTo(self.contentView).offset(8); make.width.equalTo(@(SCREEN_WIDTH - 16)); }]; label; }); 

Я заменил правильное ограничение на ограничение ширины, и предупреждение исчезло.

Есть другой способ, чтобы предупреждение не было: self.contentView.translatesAutoresizingMaskIntoConstraints = NO; , НО рамка метки неверна.

Раздражающе, похоже, что UITableViewHeaderFooterView не любит постоянные ограничения, например

| -10- [вид] -10- |

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

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

| – (<= 10) – [вид] – (<= 10) – |

Который должен удовлетворять нулевой ширине и должен давать желаемый запас при изменении размера.

Необходимо обновить frame contentView , и предупреждения исчезли. Это может быть похоже на проблему с ограничениями автоматической компоновки на iOS7 в UITableViewCell

 public class MyHeaderview: UITableViewHeaderFooterView { // Add views to contentView public override func layoutSubviews() { super.layoutSubviews() contentView.frame = bounds } } 
Interesting Posts
Давайте будем гением компьютера.