Как динамически рассчитать высоту ячейки UITableView?

«Как динамически рассчитать высоту ячейки UITableView?» — вопрос из категории UIKit, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Динамический расчет высоты ячейки основан на Auto Layout и системе вычисления размера контента. Вот основные методы:

1. Автоматический размер (iOS 8+, рекомендуемый) Самый простой способ, если ячейка использует Auto Layout.

// В viewDidLoad() таблицы
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100 // Приблизительная высота для оптимизации

// Ячейка должна иметь однозначные (non-ambiguous) constraints
// от верхнего края contentView к нижнему.
// Для многострочного UILabel установите numberOfLines = 0.

Система сама вычислит высоту на основе констрейнтов.

2. Ручной расчет с systemLayoutSizeFitting (для сложных случаев) Используется, когда автоматического расчета недостаточно или нужен точный контроль.

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    // 1. Получаем экземпляр ячейки для расчета (не отображаемый)
    let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellID") as! YourCustomCell

    // 2. Конфигурируем ячейку данными для конкретного indexPath
    cell.configure(with: dataModel[indexPath.row])

    // 3. Принудительно применяем layout
    cell.setNeedsLayout()
    cell.layoutIfNeeded()

    // 4. Вычисляем требуемый размер
    let targetSize = CGSize(width: tableView.bounds.width, height: UIView.layoutFittingCompressedSize.height)
    let calculatedSize = cell.contentView.systemLayoutSizeFitting(
        targetSize,
        withHorizontalFittingPriority: .required, // Ширина фиксирована
        verticalFittingPriority: .fittingSizeLevel // Высота подстраивается под контент
    )

    // 5. Возвращаем высоту + 1 пункт (часто для разделителя)
    return calculatedSize.height + 1.0
}

3. Для UICollectionView (FlowLayout)

if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
    layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
// Или задать точный размер через делегат
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    // ... аналогичная логика расчета
}

Критически важные условия для работы:

  • Констрейнты в ячейке должны быть полными и неконфликтующими.
  • Должна существовать непрерывная цепочка constraints от верхнего до нижнего края contentView.
  • Для многострочного текста у UILabel должно быть numberOfLines = 0 и установлен contentCompressionResistancePriority.
  • Используйте estimatedRowHeight для повышения производительности.

Совет: Для очень сложных или производительных списков рассмотрите UICollectionViewCompositionalLayout или фреймворк вроде IGListKit.