ios - automatic - uitableviewcell dynamic height swift




Использование автоматической компоновки в UITableView для динамических раскладок ячеек и переменных высот строк (17)

Быстрый пример переменной высоты UITableViewCell

Обновлено для Swift 3

Ответ Уильяма Ху Стрита хорош, но он помогает мне иметь простые, но подробные шаги, когда вы научитесь что-то делать в первый раз. Пример ниже - это мой тестовый проект, учась делать UITableView с переменной высотой ячейки. Я основал его на этом базовом примере UITableView для Swift .

Готовый проект должен выглядеть так:

Создать новый проект

Это может быть просто приложение с одним представлением.

Добавить код

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

import UIKit
class MyCustomCell: UITableViewCell {
    @IBOutlet weak var myCellLabel: UILabel!
}

Мы подключим эту розетку позже.

Откройте ViewController.swift и убедитесь, что у вас есть следующий контент:

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    // These strings will be the data for the table view cells
    let animals: [String] = [
        "Ten horses:  horse horse horse horse horse horse horse horse horse horse ",
        "Three cows:  cow, cow, cow",
        "One camel:  camel",
        "Ninety-nine sheep:  sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep baaaa sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep",
        "Thirty goats:  goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat "]

    // Don't forget to enter this in IB also
    let cellReuseIdentifier = "cell"

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // delegate and data source
        tableView.delegate = self
        tableView.dataSource = self

        // Along with auto layout, these are the keys for enabling variable cell height
        tableView.estimatedRowHeight = 44.0
        tableView.rowHeight = UITableViewAutomaticDimension
    }

    // number of rows in table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.animals.count
    }

    // create a cell for each table view row
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
        cell.myCellLabel.text = self.animals[indexPath.row]
        return cell
    }

    // method to run when table view cell is tapped
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You tapped cell number \(indexPath.row).")
    }
}

Важная заметка:

  • Это следующие две строки кода (наряду с автоматической компоновкой), которые делают возможной высоту переменной ячейки:

    tableView.estimatedRowHeight = 44.0
    tableView.rowHeight = UITableViewAutomaticDimension
    

Настройка раскадровки

Добавьте представление таблицы в контроллер просмотра и используйте автоматический макет, чтобы прикрепить его к четырем сторонам. Затем перетащите элемент Table View на представление таблицы. И в ячейку Prototype перетащите ярлык. Используйте автоматическую компоновку, чтобы привязать метку к четырем краям представления содержимого ячейки Table View.

Важная заметка:

  • Автоматическая компоновка работает вместе с важными двумя строками кода, упомянутыми выше. Если вы не используете автоматический макет, он не будет работать.

Другие настройки IB

Пользовательское имя класса и идентификатор

Выберите ячейку «Просмотр таблицы» и установите для настраиваемого класса MyCustomCell (имя класса в файле Swift, который мы добавили). Также установите идентификатор как cell (ту же строку, которую мы использовали для cellReuseIdentifier в приведенном выше коде.

Нулевые строки для метки

Установите количество строк на 0 в метке. Это означает, что многострочная линия и позволяет этикетке изменять размеры на основе ее содержимого.

Подключите розетки

  • Управляйте перетаскиванием из представления таблицы в раскадровке переменной ViewController коде ViewController .
  • Сделайте то же самое для метки в своей ячейке Prototype для переменной MyCustomCell классе MyCustomCell .

Законченный

Теперь вы сможете запустить свой проект и получить ячейки с переменной высотой.

Заметки

  • Этот пример работает только для iOS 8 и после. Если вам все еще нужно поддерживать iOS 7, то это не сработает для вас.
  • У ваших собственных пользовательских ячеек в ваших будущих проектах, вероятно, будет больше одной метки. Убедитесь, что вы все закреплены правильно, чтобы автомат мог определить правильную высоту для использования. Вы также можете использовать вертикальное сопротивление сжатию и обнимать. Подробнее см. В этой статье .
  • Если вы не привязываете передний и задний (левый и правый) края, вам также может потребоваться установить preferredMaxLayoutWidth метку preferredMaxLayoutWidth чтобы он знал, когда переносить строки. Например, если вы добавили ограничение по горизонтали по центру на метку в вышеприведенном проекте, а не нанесли передний и задний края, то вам нужно будет добавить эту строку в метод tableView:cellForRowAtIndexPath :

     cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
    

Смотрите также

Как вы используете автоматическую компоновку в UITableViewCell s в представлении таблицы, чтобы содержимое и подсечки каждой ячейки определяли высоту строки (сама / автоматически) при сохранении плавной прокрутки?


Я упаковал решение iOS7 @ smileyborg в категории

Я решил обернуть это умное решение @smileyborg в категорию UICollectionViewCell+AutoLayoutDynamicHeightCalculation .

Категория также устраняет проблемы, описанные в ответе @ wildmonkey (загрузка ячейки из systemLayoutSizeFittingSize: и systemLayoutSizeFittingSize: возврат CGRectZero )

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

UICollectionViewCell + AutoLayoutDynamicHeightCalculation.h

#import <UIKit/UIKit.h>

typedef void (^UICollectionViewCellAutoLayoutRenderBlock)(void);

/**
 *  A category on UICollectionViewCell to aid calculating dynamic heights based on AutoLayout contraints.
 *
 *  Many thanks to @smileyborg and @wildmonkey
 *
 *  @see .com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights
 */
@interface UICollectionViewCell (AutoLayoutDynamicHeightCalculation)

/**
 *  Grab an instance of the receiving type to use in order to calculate AutoLayout contraint driven dynamic height. The method pulls the cell from a nib file and moves any Interface Builder defined contrainsts to the content view.
 *
 *  @param name Name of the nib file.
 *
 *  @return collection view cell for using to calculate content based height
 */
+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name;

/**
 *  Returns the height of the receiver after rendering with your model data and applying an AutoLayout pass
 *
 *  @param block Render the model data to your UI elements in this block
 *
 *  @return Calculated constraint derived height
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width;

/**
 *  Directly calls `heightAfterAutoLayoutPassAndRenderingWithBlock:collectionViewWidth` assuming a collection view width spanning the [UIScreen mainScreen] bounds
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block;

@end

UICollectionViewCell + AutoLayoutDynamicHeightCalculation.m

#import "UICollectionViewCell+AutoLayout.h"

@implementation UICollectionViewCell (AutoLayout)

#pragma mark Dummy Cell Generator

+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name
{
    UICollectionViewCell *heightCalculationCell = [[[NSBundle mainBundle] loadNibNamed:name owner:self options:nil] lastObject];
    [heightCalculationCell moveInterfaceBuilderLayoutConstraintsToContentView];
    return heightCalculationCell;
}

#pragma mark Moving Constraints

- (void)moveInterfaceBuilderLayoutConstraintsToContentView
{
    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        [self removeConstraint:constraint];
        id firstItem = constraint.firstItem == self ? self.contentView : constraint.firstItem;
        id secondItem = constraint.secondItem == self ? self.contentView : constraint.secondItem;
        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:firstItem
                                                                     attribute:constraint.firstAttribute
                                                                     relatedBy:constraint.relation
                                                                        toItem:secondItem
                                                                     attribute:constraint.secondAttribute
                                                                    multiplier:constraint.multiplier
                                                                      constant:constraint.constant]];
    }];
}

#pragma mark Height

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block
{
    return [self heightAfterAutoLayoutPassAndRenderingWithBlock:block
                                            collectionViewWidth:CGRectGetWidth([[UIScreen mainScreen] bounds])];
}

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width
{
    NSParameterAssert(block);

    block();

    [self setNeedsUpdateConstraints];
    [self updateConstraintsIfNeeded];

    self.bounds = CGRectMake(0.0f, 0.0f, width, CGRectGetHeight(self.bounds));

    [self setNeedsLayout];
    [self layoutIfNeeded];

    CGSize calculatedSize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

    return calculatedSize.height;

}

@end

Пример использования:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MYSweetCell *cell = [MYSweetCell heightCalculationCellFromNibWithName:NSStringFromClass([MYSweetCell class])];
    CGFloat height = [cell heightAfterAutoLayoutPassAndRenderingWithBlock:^{
        [(id<MYSweetCellRenderProtocol>)cell renderWithModel:someModel];
    }];
    return CGSizeMake(CGRectGetWidth(self.collectionView.bounds), height);
}

К счастью, нам не придется делать этот джаз в iOS8, но там он пока!


Для IOS8 это очень просто:

override func viewDidLoad() {  
    super.viewDidLoad()

    self.tableView.estimatedRowHeight = 80
    self.tableView.rowHeight = UITableViewAutomaticDimension
}

ИЛИ ЖЕ

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

Но для IOS7 ключ вычисляет высоту после автовыключения,

func calculateHeightForConfiguredSizingCell(cell: GSTableViewCell) -> CGFloat {
    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize).height + 1.0
    return height
}

Важный

  • Если несколько ярлыков строк, не забудьте установить для numberOfLines значение 0 .

  • Не забывайте label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

Здесь приведен полный пример кода.

EDIT Swift 4.2 UITableViewAutomaticDimension изменен на UITableView.automaticDimension


(для Xcode 8.x / Xcode 9.x читайте внизу)

Остерегайтесь следующей проблемы в Xcode 7.x, которая может быть источником путаницы:

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

Например, представьте, что вы настроили все хорошо, никаких предупреждений, ошибок нет, все работает.

Теперь, если вы измените размер шрифта (в этом примере я изменяю размер шрифта метки описания с 17.0 до 18.0).

Поскольку размер шрифта увеличился, метка теперь займет 3 строки (до этого она занимала 2 строки).

Если Interface Builder работал должным образом, он изменил бы высоту ячейки, чтобы она соответствовала новой высоте метки. Однако на самом деле происходит то, что IB отображает красный значок ошибки автоматической компоновки и предлагает изменить параметры обхода / сжатия.

Вы должны игнорировать эти предупреждения. Вместо этого вы можете вручную изменить высоту строки (выберите Cell> Size Inspector> Row Height).

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

* Обратите внимание, что на самом деле вам не нужно разрешать эти красные ошибки или желтые предупреждения в Interface Builder - во время выполнения все будет работать правильно (даже если IB показывает ошибки / предупреждения). Просто убедитесь, что во время выполнения в журнале консоли вы не получаете ошибок AutoLayout.

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

Чтобы предотвратить раздражающие предупреждения / ошибки IB, вы можете выбрать виды, в которых участвует, и Size Inspectorдля Ambiguityвыбора свойстваVerify Position Only

Xcode 8.x / Xcode 9.x по-видимому (иногда) делает вещи иначе, чем Xcode 7.x, но все же неправильно. Например, даже когда compression resistance priority/ hugging priorityзаданы требуемые (1000), Interface Builder может растянуть или скопировать ярлык, чтобы он соответствовал ячейке (вместо изменения размера ячейки, чтобы она соответствовала метке). И в этом случае он может даже не показывать никаких предупреждений или ошибок AutoLayout. Или иногда он делает именно то, что сделал Xcode 7.x, описанный выше.


TL; DR: Не нравится читать? Подходите прямо к образцам проектов GitHub:

Концептуальное описание

Первые два шага ниже применимы независимо от того, для каких версий iOS вы разрабатываете.

1. Настройка и добавление ограничений

В вашем подклассе UITableViewCell добавьте ограничения, чтобы в подзаголовках ячейки были привязаны к краям содержимого contentView (что наиболее важно для верхних и нижних краев). ПРИМЕЧАНИЕ: не подключайте субвью к самой ячейке; только в contentView ! Пусть внутренний размер содержимого этих подзонов управляет высотой представления содержимого ячейки таблицы, убедившись, что ограничения содержимого и ограничения содержимого в вертикальном измерении для каждого поднабора не переопределяются дополнительными ограничениями с более высоким приоритетом, которые вы добавили. ( Да? Нажмите здесь. )

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

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

Как правило, ваши ограничения являются самой сложной и важной частью получения динамических высот ячеек, работающих с Auto Layout. Если вы допустили ошибку здесь, это может помешать всему остальному работать, так что не спешите! Я рекомендую настроить свои ограничения в коде, потому что вы точно знаете, какие ограничения добавляются туда, и гораздо легче отлаживать, когда что-то идет не так. Добавление ограничений в код может быть столь же простым и значительно более мощным, чем Interface Builder с использованием якорей компоновки или одним из фантастических API-интерфейсов с открытым исходным кодом, доступным в GitHub.

  • Если вы добавляете ограничения в код, вы должны сделать это один раз из метода updateConstraints вашего подкласса UITableViewCell. Обратите внимание, что updateConstraints можно вызывать более одного раза, поэтому, чтобы не добавлять одни и те же ограничения более одного раза, обязательно заверните код с добавлением ограничений в updateConstraints в проверку для логического свойства, такого как didSetupConstraints (который вы установили YES после вас запустите свой код с добавлением ограничений один раз). С другой стороны, если у вас есть код, который обновляет существующие ограничения (например, корректирует свойство constant на некоторых ограничениях), поместите это в updateConstraints но вне проверки для didSetupConstraints чтобы он мог запускаться каждый раз при вызове метода.

2. Определение уникальных идентификаторов повторного использования ячеек таблицы.

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

Например, если вы показывали сообщение электронной почты в каждой ячейке, у вас могло бы быть 4 уникальных макета: сообщения с только субъектом, сообщения с темой и телом, сообщения с объектом и прикреплением фотографий, а также сообщения с объектом, тела и фотографии. Каждый макет имеет совершенно разные ограничения, необходимые для его достижения, поэтому после инициализации ячейки и добавления ограничений для одного из этих типов ячеек ячейка должна получить уникальный идентификатор повторного использования, специфичный для этого типа ячейки. Это означает, что когда вы удаляете ячейку для повторного использования, ограничения уже добавлены и готовы к использованию для этого типа ячейки.

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

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

Для iOS 8 - Self-Sizing Cells

3. Включение оценки высоты строки

Чтобы включить ячейки отображения таблицы размеров, вы должны установить свойство rowHeight таблицы в UITableViewAutomaticDimension. Вы также должны назначить значение для свойства measuredRowHeight. Как только оба этих свойства установлены, система использует автоматический макет для вычисления фактической высоты строки

Apple: работа с ячейками просмотра таблицы калибровки

В iOS 8 Apple усвоила большую часть работы, которая ранее должна была быть реализована вами до iOS 8. Чтобы позволить механизму ячеек для самостоятельной калибровки работать, вы должны сначала установить свойство rowHeight в представлении таблицы на постоянный UITableViewAutomaticDimension . Затем вам просто нужно включить оценку высоты строки, установив для свойства valueRowHeight табличного представления значение, отличное от нуля, например:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is

Это означает, что табличное представление содержит временную оценку / местозаполнитель для высоты строк ячеек, которые еще не отображаются на экране. Затем, когда эти ячейки будут перемещаться по экрану, будет подсчитана фактическая высота строки. Чтобы определить фактическую высоту для каждой строки, табличное представление автоматически запрашивает каждую ячейку, на какой высоте ее contentView должен быть основан на известной фиксированной ширине представления содержимого (которая основана на ширине табличного представления, минус любые дополнительные вещи, такие как раздел индекс или аксессуар) и ограничения автоматического макета, которые вы добавили в контентное представление и подпункты ячейки. После определения фактической высоты ячейки старая оцененная высота для строки обновляется с новой фактической высотой (и любые корректировки в contentSize / contentOffset для табличного представления создаются по мере необходимости для вас).

Вообще говоря, предоставленная вами оценка не обязательно должна быть очень точной - она ​​используется только для правильного размера индикатора прокрутки в представлении таблицы, и в представлении таблицы хорошая работа по настройке индикатора прокрутки для неправильных оценок, поскольку вы прокручивать ячейки на экране. Вы должны установить свойство estimatedRowHeight в представлении таблицы (в viewDidLoad или аналогичном) с постоянным значением, которое является «средней» высотой строки. Только если ваши высоты строк имеют экстремальную изменчивость (например, отличаются на порядок), и вы заметите, что индикатор прокрутки «прыгает», когда вы прокручиваете, вы должны беспокоиться о внедрении tableView:estimatedHeightForRowAtIndexPath: выполнить минимальный расчет, необходимый для возврата более точной оценки для каждый ряд.

Для поддержки iOS 7 (для самой автоматической настройки размеров ячеек)

3. Сделайте прокладку макета и получите высоту ячейки

Во-первых, создайте экземпляр внеэкранного экземпляра ячейки представления таблицы, один экземпляр для каждого идентификатора повторного использования , который используется строго для расчетов высоты. (Offscreen означает, что ссылка на ячейку хранится в свойстве / ivar на контроллере представления и никогда не возвращается из tableView:cellForRowAtIndexPath: для представления таблицы для фактического отображения на экране.) Далее ячейка должна быть настроена с точным контентом (например, текст, изображения и т. д.), которые он будет удерживать, если они будут отображаться в виде таблицы.

Затем принудительно systemLayoutSizeFittingSize: ячейку для немедленного раскладки своих подзонов, а затем используйте метод systemLayoutSizeFittingSize: в systemLayoutSizeFittingSize: UITableViewCell , чтобы узнать, какова требуемая высота ячейки. Используйте UILayoutFittingCompressedSize чтобы получить минимальный размер, необходимый для размещения всего содержимого ячейки. Затем высота может быть возвращена из tableView:heightForRowAtIndexPath: delegate.

4. Используйте приблизительные высоты строк

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

Начиная с iOS 7, вы можете (и абсолютно должны) использовать свойство ratedRowHeight в представлении таблицы. Это означает, что табличное представление содержит временную оценку / местозаполнитель для высоты строк ячеек, которые еще не отображаются на экране. Затем, когда эти ячейки собираются прокручивать экран, фактическая высота строки будет вычисляться (путем вызова tableView:heightForRowAtIndexPath: и оцененной высоты, обновленной с фактической.

Вообще говоря, предоставленная вами оценка не обязательно должна быть очень точной - она ​​используется только для правильного размера индикатора прокрутки в представлении таблицы, и в представлении таблицы хорошая работа по настройке индикатора прокрутки для неправильных оценок, поскольку вы прокручивать ячейки на экране. Вы должны установить свойство estimatedRowHeight в представлении таблицы (в viewDidLoad или аналогичном) с постоянным значением, которое является «средней» высотой строки. Только если ваши высоты строк имеют экстремальную изменчивость (например, отличаются на порядок), и вы заметите, что индикатор прокрутки «прыгает», когда вы прокручиваете, вы должны беспокоиться о внедрении tableView:estimatedHeightForRowAtIndexPath: выполнить минимальный расчет, необходимый для возврата более точной оценки для каждый ряд.

5. (если необходимо) Добавить кеширование высоты строки

Если вы сделали все вышеизложенное и все еще находите, что производительность неприемлемо медленна при решении ограничений в tableView:heightForRowAtIndexPath: вам, к сожалению, нужно будет реализовать некоторое кэширование для высоты ячейки. (Это подход, предложенный инженерами Apple.) Основная идея заключается в том, чтобы позволить автоматическому механизму компоновки разрешать ограничения в первый раз, затем кэшировать рассчитанную высоту для этой ячейки и использовать кешированное значение для всех будущих запросов для высоты этой ячейки. Трюк, конечно же, заключается в том, чтобы убедиться, что вы очищаете кешированную высоту для ячейки, когда что-то происходит, что может привести к изменению высоты ячейки - в первую очередь это произойдет, когда содержимое этой ячейки изменится или когда произойдут другие важные события (например, пользовательская настройка ползунок размера текста динамического типа).

Общий код образца iOS 7 (с большим количеством сочных комментариев)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path, depending on the particular layout required (you may have
    // just one, or may have many).
    NSString *reuseIdentifier = ...;

    // Dequeue a cell for the reuse identifier.
    // Note that this method will init and return a new cell if there isn't
    // one available in the reuse pool, so either way after this line of 
    // code you will have a cell with the correct constraints ready to go.
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // If you are using multi-line UILabels, don't forget that the 
    // preferredMaxLayoutWidth needs to be set correctly. Do it at this 
    // point if you are NOT doing it within the UITableViewCell subclass 
    // -[layoutSubviews] method. For example: 
    // cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path.
    NSString *reuseIdentifier = ...;

    // Use a dictionary of offscreen cells to get a cell for the reuse 
    // identifier, creating a cell and storing it in the dictionary if one 
    // hasn't already been added for the reuse identifier. WARNING: Don't 
    // call the table view's dequeueReusableCellWithIdentifier: method here 
    // because this will result in a memory leak as the cell is created but 
    // never returned from the tableView:cellForRowAtIndexPath: method!
    UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
    if (!cell) {
        cell = [[YourTableViewCellClass alloc] init];
        [self.offscreenCells setObject:cell forKey:reuseIdentifier];
    }

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // Set the width of the cell to match the width of the table view. This
    // is important so that we'll get the correct cell height for different
    // table view widths if the cell's height depends on its width (due to 
    // multi-line UILabels word wrapping, etc). We don't need to do this 
    // above in -[tableView:cellForRowAtIndexPath] because it happens 
    // automatically when the cell is used in the table view. Also note, 
    // the final width of the cell may not be the width of the table view in
    // some cases, for example when a section index is displayed along 
    // the right side of the table view. You must account for the reduced 
    // cell width.
    cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));

    // Do the layout pass on the cell, which will calculate the frames for 
    // all the views based on the constraints. (Note that you must set the 
    // preferredMaxLayoutWidth on multi-line UILabels inside the 
    // -[layoutSubviews] method of the UITableViewCell subclass, or do it 
    // manually at this point before the below 2 lines!)
    [cell setNeedsLayout];
    [cell layoutIfNeeded];

    // Get the actual height required for the cell's contentView
    CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    // Add an extra point to the height to account for the cell separator, 
    // which is added between the bottom of the cell's contentView and the 
    // bottom of the table view cell.
    height += 1.0;

    return height;
}

// NOTE: Set the table view's estimatedRowHeight property instead of 
// implementing the below method, UNLESS you have extreme variability in 
// your row heights and you notice the scroll indicator "jumping" 
// as you scroll.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Do the minimal calculations required to be able to return an 
    // estimated row height that's within an order of magnitude of the 
    // actual height. For example:
    if ([self isTallCellAtIndexPath:indexPath]) {
        return 350.0;
    } else {
        return 40.0;
    }
}

Примеры проектов

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

Xamarin (C # /. NET)

Если вы используете Xamarin, посмотрите этот примерный проект, составленный @KentBoogaart .


В моем случае отступы были из-за высот разделаHeader и sectionFooter, где раскадровка позволяла мне изменить его на минимум 1. Таким образом, в методе viewDidLoad:

tableView.sectionHeaderHeight = 0
tableView.sectionFooterHeight = 0

Если вы используете программный подход, вот что следует учитывать для iOS 10 с использованием якорей в Swift.

Существует три правила / этапы

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

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 100

NUMBER 2: важно, чтобы вы добавляли subviews в contentView ячейки не к представлению, а также использовали его layoutsmarginguide для привязки subviews к началу и снизу, это рабочий пример того, как это сделать.

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    setUpViews()
}

private func setUpViews() {

    contentView.addSubview(movieImageView)
    contentView.addSubview(descriptionLabel)
    let marginGuide = contentView.layoutMarginsGuide

    NSLayoutConstraint.activate([
        movieImageView.heightAnchor.constraint(equalToConstant: 80),
        movieImageView.widthAnchor.constraint(equalToConstant: 80),
        movieImageView.leftAnchor.constraint(equalTo: marginGuide.leftAnchor),
        movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 20),

        descriptionLabel.leftAnchor.constraint(equalTo: movieImageView.rightAnchor, constant: 15),
        descriptionLabel.rightAnchor.constraint(equalTo: marginGuide.rightAnchor),
        descriptionLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -15),
        descriptionLabel.topAnchor.constraint(equalTo: movieImageView.topAnchor)

        ])
}

Создайте метод, который добавит subviews и выполнит макет, вызовите его в методе init.

НОМЕР 3: НЕ ПРИЗЫВАЙТЕ МЕТОД:

  override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    }

Если вы это сделаете, вы переопределите свою реализацию.

Следуйте этим 3 правилам для динамических ячеек в табличных представлениях.

вот рабочая реализация https://github.com/jamesrochabrun/MinimalViewController


Еще одно «решение»: пропустите все это разочарование и вместо этого используйте UIScrollView, чтобы получить результат, который выглядит и чувствует себя идентичным UITableView.

Это было болезненное «решение» для меня, после того, как мы поставили буквально 20+ очень расстраивающих часов, пытаясь построить что-то вроде того, что предлагалось и не хватало smileyborg в течение многих месяцев и трех версий выпусков App Store.

Я считаю, что если вам действительно нужна поддержка iOS 7 (для нас это важно), то технология слишком хрупка, и вы будете пытаться вытянуть свои волосы. И что UITableView полностью переборщил, если вы не используете некоторые из усовершенствованных функций редактирования строк и / или действительно нуждаетесь в поддержке 1000+ «строк» ​​(в нашем приложении это реалистично не более 20 строк).

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

Вот несколько советов о том, как это сделать:

1) Используя либо Storyboard, либо файл nib, создайте ViewController и связанный с ним корневой вид.

2) Перетащите UIScrollView на корневой режим.

3) Добавьте ограничения верхнего, нижнего, левого и правого к представлению верхнего уровня, чтобы UIScrollView заполнил весь корневой вид.

4) Добавьте UIView внутри UIScrollView и назовите его «контейнер». Добавьте ограничения сверху, снизу, слева и справа в UIScrollView (его родительский элемент). KEY TRICK: Также добавьте ограничения «Равная ширина», чтобы связать UIScrollView и UIView.

Вы получите сообщение об ошибке «просмотр прокрутки имеет неоднозначную высоту прокручиваемого содержимого» и что ваш UIView для контейнера должен иметь высоту 0 пикселей. Ни одна ошибка не имеет значения, когда приложение работает.

5) Создайте nib-файлы и контроллеры для каждой из ваших «ячеек». Используйте UIView, а не UITableViewCell.

5) В корневом ViewController вы по существу добавляете все «строки» в контейнер UIView и программно добавляете ограничения, связывающие их левый и правый края с видом контейнера, их верхние края либо сверху, либо в верхнем ящике контейнера (для первого элемента), либо предыдущей ячейки. Затем свяжите последнюю ячейку с дном контейнера.

Для нас каждая «строка» находится в файле nib. Таким образом, код выглядит примерно так:

class YourRootViewController {

    @IBOutlet var container: UIView! //container mentioned in step 4

    override func viewDidLoad() {

        super.viewDidLoad()

        var lastView: UIView?
        for data in yourDataSource {

            var cell = YourCellController(nibName: "YourCellNibName", bundle: nil)
            UITools.addViewToTop(container, child: cell.view, sibling: lastView)
            lastView = cell.view
            //Insert code here to populate your cell
        }

        if(lastView != nil) {
            container.addConstraint(NSLayoutConstraint(
                item: lastView!,
                attribute: NSLayoutAttribute.Bottom,
                relatedBy: NSLayoutRelation.Equal,
                toItem: container,
                attribute: NSLayoutAttribute.Bottom,
                multiplier: 1,
                constant: 0))
        }

        ///Add a refresh control, if you want - it seems to work fine in our app:
        var refreshControl = UIRefreshControl()
        container.addSubview(refreshControl!)
    }
}

И вот код для UITools.addViewToTop:

class UITools {
    ///Add child to container, full width of the container and directly under sibling (or container if sibling nil):
    class func addViewToTop(container: UIView, child: UIView, sibling: UIView? = nil)
    {
        child.setTranslatesAutoresizingMaskIntoConstraints(false)
        container.addSubview(child)

        //Set left and right constraints so fills full horz width:

        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Leading,
            relatedBy: NSLayoutRelation.Equal,
            toItem: container,
            attribute: NSLayoutAttribute.Left,
            multiplier: 1,
            constant: 0))

        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Trailing,
            relatedBy: NSLayoutRelation.Equal,
            toItem: container,
            attribute: NSLayoutAttribute.Right,
            multiplier: 1,
            constant: 0))

        //Set vertical position from last item (or for first, from the superview):
        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Top,
            relatedBy: NSLayoutRelation.Equal,
            toItem: sibling == nil ? container : sibling,
            attribute: sibling == nil ? NSLayoutAttribute.Top : NSLayoutAttribute.Bottom,
            multiplier: 1,
            constant: 0))
    }
}

Единственный «полученный», который я нашел с этим подходом до сих пор, заключается в том, что UITableView имеет приятную функцию «плавающих» заголовков разделов в верхней части представления при прокрутке. Вышеупомянутое решение не будет делать этого, если вы не добавите больше программ, но для нашего конкретного случая эта функция не была на 100% существенной, и никто не заметил, когда она ушла.

Если вы хотите делителей между вашими ячейками, просто добавьте UIView с высотой 1 пиксель в нижней части пользовательской «ячейки», которая выглядит как разделитель.

Обязательно включите «отскоки» и «отскок вертикально», чтобы управление обновлением работало, и поэтому оно больше похоже на табличное представление.

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

Здесь надеется, что какой-то другой программист прочитает мой пост, прежде чем тратить 20 + часов, пытаясь понять это с помощью Table View в своем собственном приложении. :)


Пока ваш макет в вашей камере хорош.

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

    return [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}

Обновление. Вы должны использовать динамическое изменение размера, введенное в iOS 8.


Предположим, у вас есть ячейка с subview, и вы хотите, чтобы высота ячейки была достаточно высокой, чтобы охватить subview + padding.

1) Установите нижнее ограничение subview, равное cell.contentView, за вычетом нужного дополнения. Не устанавливайте ограничения на элемент cell или cell.contentView.

2) Установите либо Tableview в rowHeightсобственность или tableView:heightForRowAtIndexPath:в UITableViewAutomaticDimension.

3) Установите либо estimatedRowHeightсвойство tableView, либо tableView:estimatedHeightForRowAtIndexPath:лучшее представление о высоте.

Вот и все.


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

  • Присвоить и реализовать данные tableviewSource и делегировать
  • Назначить UITableViewAutomaticDimensionrowHeight & оценкамRowHeight
  • Внедрить методы делегирования / dataSource (т.е. heightForRowAtи вернуть ему значение UITableViewAutomaticDimension)

-

Цель C:

// in ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

  @property IBOutlet UITableView * table;

@end

// in ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    self.table.dataSource = self;
    self.table.delegate = self;

    self.table.rowHeight = UITableViewAutomaticDimension;
    self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    return UITableViewAutomaticDimension;
}

Swift:

@IBOutlet weak var table: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Don't forget to set dataSource and delegate for table
    table.dataSource = self
    table.delegate = self

    // Set automatic dimensions for row height
    // Swift 4.2 onwards
    table.rowHeight = UITableView.automaticDimension
    table.estimatedRowHeight = UITableView.automaticDimension


    // Swift 4.1 and below
    table.rowHeight = UITableViewAutomaticDimension
    table.estimatedRowHeight = UITableViewAutomaticDimension

}



// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    // Swift 4.2 onwards
    return UITableView.automaticDimension

    // Swift 4.1 and below
    return UITableViewAutomaticDimension
}

Для экземпляра ярлыка в UITableviewCell

  • Установить число строк = 0 (& режим разрыва строки = обрезать хвост)
  • Задайте все ограничения (сверху, снизу, справа налево) относительно контейнера супервизора / ячейки.
  • Необязательно : установите минимальную высоту для метки, если вы хотите, чтобы минимальная вертикальная область была покрыта меткой, даже если данных нет.

Примечание . Если у вас есть несколько ярлыков (UIElements) с динамической длиной, которые должны быть скорректированы в соответствии с его размером содержимого: Настройте «Охват содержимого и приоритет сжатия» для меток, которые вы хотите расширить / сжать с более высоким приоритетом.


еще одно решение iOs7 + iOs8 в Swift

var cell2height:CGFloat=44

override func viewDidLoad() {
    super.viewDidLoad()
    theTable.rowHeight = UITableViewAutomaticDimension
    theTable.estimatedRowHeight = 44.0;
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell =  tableView.dequeueReusableCellWithIdentifier("myTableViewCell", forIndexPath: indexPath) as! myTableViewCell
    cell2height=cell.contentView.height
    return cell
}

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if #available(iOS 8.0, *) {
        return UITableViewAutomaticDimension
    } else {
        return cell2height
    }
}

В случае, если у людей все еще есть проблемы с этим. Я написал краткое сообщение в блоге об использовании Autolayout с помощью UITableViews « Использование Autolayout for Dynamic Cell Heights», а также компонент с открытым исходным кодом, который поможет сделать его более абстрактным и более простым в реализации. https://github.com/Raizlabs/RZCellSizeManager


Если у вас длинная строка. например, тот, который не имеет разрыва строки. Тогда вы можете столкнуться с некоторыми проблемами.

Исправление упоминается принятым ответом и несколькими другими ответами. Вам просто нужно добавить

cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width

Я нахожу ответ Сурага наиболее полным, будучи наименее запутанным.

Хотя не объяснять почему . Давайте сделаем это.

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

import UIKit

class ViewController: UIViewController {

    lazy var label : UILabel = {
        let lbl = UILabel()
        lbl.translatesAutoresizingMaskIntoConstraints = false
        lbl.backgroundColor = .red
        lbl.textColor = .black
        return lbl
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // step0: (0.0, 0.0)
        print("empty Text intrinsicContentSize: \(label.intrinsicContentSize)")
        // step1: (29.0, 20.5)
        label.text = "hiiiii"
        print("hiiiii intrinsicContentSize: \(label.intrinsicContentSize)")
        // step2: (328.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints"
        print("1 translate intrinsicContentSize: \(label.intrinsicContentSize)")
        // step3: (992.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints"
        print("3 translate intrinsicContentSize: \(label.intrinsicContentSize)")
        // step4: (328.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints"
        print("3 translate w/ line breaks intrinsicContentSize: \(label.intrinsicContentSize)") 
        // step5: (328.0, 61.0)
        label.numberOfLines = 0
        print("3 translate w/ line breaks and '0' numberOfLines intrinsicContentSize: \(label.intrinsicContentSize)") 
        // step6: (98.5, 243.5)
        label.preferredMaxLayoutWidth = 100
        print("3 translate w/ line breaks | '0' numberOfLines | preferredMaxLayoutWidth: 100 intrinsicContentSize: \(label.intrinsicContentSize)") 

        setupLayout()
    }
    func setupLayout(){
        view.addSubview(label)
        label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }
}

Обратите внимание, что я не добавил ограничений по размеру . Я добавил только ограничения centerX, centerY. Но все же метка будет правильно подсчитана. Почему? Из-за contentSize.

Чтобы лучше обработать это, сначала сделайте шаг0, затем прокомментируйте шаги 1-6. Пусть setupLayout()останется. Наблюдайте за поведением.

Затем раскомментируйте шаг 1 и наблюдайте.

Затем раскомментируйте шаг 2 и соблюдайте.

Сделайте это, пока вы не раскомментируете все 6 шагов и не заметите их поведения.

Что можно сделать из всего этого? Какие факторы могут изменить contenSize?

  1. Длина текста. Если у вас есть более длинный текст, ширина вашей внутренней строкиContentSize будет увеличиваться
  2. Разрывы строк: если вы добавите \n, ширина intrinsicContentSize будет максимальной шириной всех строк. Если одна строка имеет 25 символов, другая имеет 2 символа, а другая - 21 символ, тогда ваша ширина будет рассчитана на 25 символов
  3. Количество разрешенных линий: Вы должны установить numberOfLinesв 0противном случае вы не будете иметь несколько строк. Ваша numberOfLinesнастройка будет настраиваться на внутреннюю высоту
  4. Корректировки. Представьте, что на основе вашего текста была ваша ширина 200и внутренняя ширина intrinsicContentSize 100, но вы хотели ограничить ширину контейнера ярлыка, что вы собираетесь делать? Решение состоит в том, чтобы установить его на желаемую ширину. Вы делаете это, устанавливая, preferredMaxLayoutWidthчтобы 130ваш новый intrinsicContentSize имел ширину примерно 130. Высота, очевидно, будет больше, чем 100потому, что вам понадобится больше строк. При этом, если ваши ограничения установлены правильно, вам не нужно будет использовать это вообще! Подробнее об этом см. В этом ответе и его комментариях. Вам нужно использовать только в том preferredMaxLayoutWidthслучае, если у вас нет ограничений, ограничивающих ширину / высоту, как можно было бы сказать: «Не завершайте текст, если он не превышаетpreferredMaxLayoutWidth». Но с 100% уверенностью , если вы установите ведущий / ведомую и numberOfLinesв 0то вы хорошо! Короче говоря большинство ответов здесь , которые рекомендуют использовать его ошибается! Вам не нужны. Нуждаясь это является признаком того, что ваши ограничения не установлены правильно или что у вас просто нет ограничений

  5. Размер шрифта. Также обратите внимание, что если вы увеличите размер шрифта, то высота встроенного содержимого будет увеличиваться. Я не показывал это в своем коде. Вы можете попробовать это самостоятельно.

Вернемся к примеру TableViewCell:

Все , что вам нужно сделать , это установить , numberOfLinesчтобы 0и установить preferredMaxLayoutWidthна свой tableView«с width.


Просто добавьте эти две функции в свой контроллер управления, и это решит вашу проблему. Здесь список - это строковый массив, который содержит строку каждой строки.

 func tableView(_ tableView: UITableView, 
   estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        tableView.rowHeight = self.calculateHeight(inString: list[indexPath.row])

    return (tableView.rowHeight) 
}

func calculateHeight(inString:String) -> CGFloat
{
    let messageString = input.text
    let attributes : [NSAttributedStringKey : Any] = [NSAttributedStringKey(rawValue: NSAttributedStringKey.font.rawValue) : UIFont.systemFont(ofSize: 15.0)]

    let attributedString : NSAttributedString = NSAttributedString(string: messageString!, attributes: attributes)

    let rect : CGRect = attributedString.boundingRect(with: CGSize(width: 222.0, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)

    let requredSize:CGRect = rect
    return requredSize.height
}

Решение, предложенное @smileyborg, почти идеально. Если у вас есть пользовательская ячейка, и вы хотите один или несколько UILabelс динамическими высотами, тогда метод systemLayoutSizeFittingSize в сочетании с включенным AutoLayout возвращает a, CGSizeZeroесли вы не переместите все ограничения соты из ячейки в свой контентный вид (как это предложено в @TomSwift здесь. Как изменить размер супервизора на подходит для всех подзапросов с автозапуском? ).

Для этого вам нужно вставить следующий код в свою пользовательскую реализацию UITableViewCell (благодаря @Adrian).

- (void)awakeFromNib{
    [super awakeFromNib];
    for (NSLayoutConstraint *cellConstraint in self.constraints) {
        [self removeConstraint:cellConstraint];
        id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
        id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
        NSLayoutConstraint *contentViewConstraint =
        [NSLayoutConstraint constraintWithItem:firstItem
                                 attribute:cellConstraint.firstAttribute
                                 relatedBy:cellConstraint.relation
                                    toItem:seccondItem
                                 attribute:cellConstraint.secondAttribute
                                multiplier:cellConstraint.multiplier
                                  constant:cellConstraint.constant];
        [self.contentView addConstraint:contentViewConstraint];
    }
}

Смешение ответа @smileyborg с этим должно работать.


tableView.estimatedRowHeight = 343.0
tableView.rowHeight = UITableViewAutomaticDimension







row-height