SetNeedsLayout ретранслирует ячейку только после того, как был отображен

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

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

if (someFlag) {
    // This causes layout to be invalidated
    [flagIcon removeFromSuperview];
    [cell setNeedsLayout];
}

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

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

Почему повторная компоновка ячейки не полностью после setNeedsLayout, но до ее отображения? Должна ли UIKit проверять недопустимые макеты перед отображением?

Если я делаю

if (someFlag) {
    [flagIcon removeFromSuperview];
    [cell layoutIfNeeded];
}

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


Еще один код о создании ячейки:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ProfileCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    [cell setData:model.items[indexPath.row] forMyself:YES];
    return cell;
}

// And in ProfileCell:
- (void)setData:(Entity *)data forMyself:(BOOL)forMe
{
    self.entity = data;

    [self.problematicLabel setText:data.attributedBody];
    // Set data in other subviews as well

    if (forMe) {
        // This causes layouts to be invalidated, and problematicLabel should resize
        [self.reportButton removeFromSuperview];
        [self layoutIfNeeded];
    }
}

Кроме того, если это имеет значение, в ячейке раскадровки выглядит так: при удалении необязательного ограничения после удаления значка флажка: Constraints in the storyboard

Ответ 1

Я согласен, что вызов layoutIfNeeded кажется неправильным, даже если он работает в вашем случае. Но я сомневаюсь, что вы что-то упустили. Хотя я не проводил никаких исследований по этому способу, мой опыт использования Auto Layout в ячейках таблицы, которые подвергаются динамической компоновке, немного ошибочен. То есть, я вижу херкие отрывистые макеты при удалении или добавлении subviews в ячейки во время выполнения.

Если вы ищете альтернативную стратегию (используя автоматический макет), вы можете подклассифицировать UITableViewCell и переопределить layoutSubviews. Пользовательская ячейка таблицы может выставить флаг в своем общедоступном API, который может быть установлен в реализации tableView:cellForRowAtIndexPath:. Метод cell layoutSubviews использовал бы флаг, чтобы определить, должен ли он включать дополнительный элемент пользовательского интерфейса. Я не гарантирую, что это устранит проблему.

Вторая стратегия состоит в том, чтобы создать два отдельных типа ячеек и поменять местами между ними в tableView:cellForRowAtIndexPath: по мере необходимости.

Ответ 2

Вы добавили дополнительный код к вопросу, поэтому у меня есть другое предложение. В методе setData:forMyself: ячейки попробуйте вызвать setNeedsUpdateConstraints вместо layoutIfNeeded.