Изменение размера UITableView для соответствия содержимому

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

Я хотел бы найти работу sizeToFit для таблицы. Если рамка таблицы установлена ​​на высоту всего содержимого.

Может ли кто-нибудь посоветовать, как я могу это достичь?

Ответ 1

На самом деле я сам нашел ответ.

Я просто создаю новый CGRect для tableView.frame с height из table.contentSize.height

Это устанавливает высоту UITableView в height ее содержимого. Поскольку код изменяет пользовательский интерфейс, не забудьте запустить его в основном потоке:

dispatch_async(dispatch_get_main_queue(), ^{
        //This code will run in the main thread:
        CGRect frame = self.tableView.frame;
        frame.size.height = self.tableView.contentSize.height;
        self.tableView.frame = frame;
    });

Ответ 2

Решение Swift 5 и 4.2 без KVO, DispatchQueue или установки ограничений самостоятельно.

Это решение основано на ответе Гульца.

1) Создайте подкласс UITableView:

import UIKit

final class ContentSizedTableView: UITableView {
    override var contentSize:CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

2) Добавьте UITableView к вашему макету и установите ограничения со всех сторон. Установите его класс ContentSizedTableView.

3) Вы должны увидеть некоторые ошибки, потому что Storyboard не учитывает intrinsicContentSize нашего подкласса. Исправьте это, открыв инспектор размера и переопределив intrinsicContentSize для значения заполнителя. Это переопределение для времени разработки. Во время выполнения он будет использовать переопределение в нашем классе ContentSizedTableView


Обновление: изменен код для Swift 4.2. Если вы используете предыдущую версию, используйте UIViewNoIntrinsicMetric вместо UIView.noIntrinsicMetric

Ответ 3

Быстрое решение

Следуй этим шагам:

1- Установите ограничение высоты таблицы для раскадровки.

2- Перетащите ограничение высоты из раскадровки и создайте @IBOutlet для него в файле контроллера вида.

    @IBOutlet weak var tableHeight: NSLayoutConstraint!

3- Затем вы можете изменить высоту таблицы динамически с помощью этого кода:

override func viewWillLayoutSubviews() {
    super.updateViewConstraints()
    self.tableHeight?.constant = self.table.contentSize.height
}

Обновить

Если последняя строка вырезана для вас, попробуйте вызвать viewWillLayoutSubviews() в функции ячейки viewDisplay:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    self.viewWillLayoutSubviews()
}

Ответ 4

Я пробовал это в iOS 7, и это сработало для меня

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView sizeToFit];
}

Ответ 5

Добавить наблюдателя для свойства contentSize в представлении таблицы и соответствующим образом скорректировать размер кадра

[your_tableview addObserver:self forKeyPath:@"contentSize" options:0 context:NULL];

то в обратном вызове:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
         CGRect frame = your_tableview.frame;
         frame.size = your_tableview.contentSize;
         your_tableview.frame = frame;
    }

Надеюсь, это поможет вам.

Ответ 6

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

protocol ContentFittingTableViewDelegate: UITableViewDelegate {
    func tableViewDidUpdateContentSize(_ tableView: UITableView)
}

class ContentFittingTableView: UITableView {

    override var contentSize: CGSize {
        didSet {
            if !constraints.isEmpty {
                invalidateIntrinsicContentSize()
            } else {
                sizeToFit()
            }

            if contentSize != oldValue {
                if let delegate = delegate as? ContentFittingTableViewDelegate {
                    delegate.tableViewDidUpdateContentSize(self)
                }
            }
        }
    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        return contentSize
    }
}

Ответ 7

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

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

1) создать подкласс UITableView:

class IntrinsicTableView: UITableView {

    override var contentSize:CGSize {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        self.layoutIfNeeded()
        return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
    }

}

2) установить класс табличного представления в раскадровке как IntrinsicTableView: снимок экрана: http://joxi.ru/a2XEENpsyBWq0A

3) Установите высоту ограничения для вашего вида таблицы

4) перетащите IBoutlet вашей таблицы на ваш ViewController

5) перетащите IBoutlet вашего ограничения высоты таблицы в ваш ViewController

6) добавьте этот метод в ваш ViewController:

override func viewWillLayoutSubviews() {
        super.updateViewConstraints()
        self.yourTableViewsHeightConstraint?.constant = self.yourTableView.intrinsicContentSize.height
    }

Надеюсь это поможет

Ответ 8

В случае, если ваш contentSize не прав, это потому, что он основан на оценочной RowHeight (автоматически), используйте это до

tableView.estimatedRowHeight = 0;

источник: https://forums.developer.apple.com/thread/81895

Ответ 9

Swift 3, iOS 10.3

Решение 1: Просто поставьте self.tableview.sizeToFit() в cellForRowAt indexPath функцию. Не забудьте установить высоту стола выше, чем вам нужно. Это хорошее решение, если у вас нет представлений ниже таблицы. Однако, если у вас есть, нижнее ограничение таблицы не будет обновляться (я не пытался его исправить, потому что я придумал решение 2)

Пример:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as? TestCell {
        cell.configureCell(data: testArray[indexPath.row])
        self.postsTableView.sizeToFit()
        return cell
    }

    return UITableViewCell()
}

Решение 2: Установите ограничение высоты таблицы в раскадровке и перетащите ее в ViewController. Если вы знаете среднюю высоту своей ячейки и знаете, сколько элементов содержит ваш массив, вы можете сделать что-то вроде этого:

tableViewHeightConstraint.constant = CGFloat(testArray.count) * 90.0     // Let say 90 is the average cell height

* EDIT:

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

Ответ 10

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

Итак. Я немного изменил ответ:

dispatch_async(dispatch_get_main_queue()) {
    //This code will run in the main thread:
    CGFloat newHeight=self.tableView.contentSize.height;
    CGFloat screenHeightPermissible=(self.view.bounds.size.height-self.tableView.frame.origin.y);
    if (newHeight>screenHeightPermissible)
    {
        //so that table view remains scrollable when 'newHeight'  exceeds the screen bounds
        newHeight=screenHeightPermissible;
    }

    CGRect frame = self.tableView.frame;
    frame.size.height = newHeight;
    self.tableView.frame = frame;
}

Ответ 11

Существует намного лучший способ сделать это, если вы используете AutoLayout: измените ограничение, определяющее высоту. Просто вычислите высоту содержимого вашей таблицы, затем найдите ограничение и измените его. Вот пример (если предположить, что ограничение, определяющее высоту таблицы, на самом деле является ограничителем высоты с отношением "Равно" ):

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    for constraint in tableView.constraints {
        if constraint.firstItem as? UITableView == tableView {
            if constraint.firstAttribute == .height {
                constraint.constant = tableView.contentSize.height
            }
        }
    }
}

Ответ 12

objc версия Musa almatri

(void)viewWillLayoutSubviews
{
    [super updateViewConstraints];
    CGFloat desiredHeight = self.tableView.contentSize.height;
    // clamp desired height, if needed, and, in that case, leave scroll Enabled
    self.tableHeight.constant = desiredHeight;
    self.tableView.scrollEnabled = NO;
}

Ответ 13

Вы можете попробовать этот пользовательский AGTableView

Установка ограничения высоты таблицы TableView с помощью раскадровки или программным способом. (Этот класс автоматически выбирает ограничение высоты и устанавливает высоту представления контента на высоту вашего стола).

class AGTableView: UITableView {

    fileprivate var heightConstraint: NSLayoutConstraint!

    override init(frame: CGRect, style: UITableViewStyle) {
        super.init(frame: frame, style: style)
        self.associateConstraints()
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.associateConstraints()
    }

    override open func layoutSubviews() {
        super.layoutSubviews()

        if self.heightConstraint != nil {
            self.heightConstraint.constant = self.contentSize.height
        }
        else{
            self.sizeToFit()
            print("Set a heightConstraint to Resizing UITableView to fit content")
        }
    }

    func associateConstraints() {
        // iterate through height constraints and identify

        for constraint: NSLayoutConstraint in constraints {
            if constraint.firstAttribute == .height {
                if constraint.relation == .equal {
                    heightConstraint = constraint
                }
            }
        }
    }
}

Примечание. Если yourTableView.layoutSubviews() проблема с установкой высоты, то yourTableView.layoutSubviews().

Ответ 14

Как расширение ответа Anooj VM, я предлагаю следующее обновить размер содержимого только тогда, когда он изменится.

Этот подход также отключает прокрутку и поддерживает более крупные списки и ротация. Нет необходимости dispatch_async, потому что изменения contentSize отправляются в основном потоке.

- (void)viewDidLoad {
        [super viewDidLoad];
        [self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:NULL]; 
}


- (void)resizeTableAccordingToContentSize:(CGSize)newContentSize {
        CGRect superviewTableFrame  = self.tableView.superview.bounds;
        CGRect tableFrame = self.tableView.frame;
        BOOL shouldScroll = newContentSize.height > superviewTableFrame.size.height;
        tableFrame.size = shouldScroll ? superviewTableFrame.size : newContentSize;
        [UIView animateWithDuration:0.3
                                    delay:0
                                    options:UIViewAnimationOptionCurveLinear
                                    animations:^{
                            self.tableView.frame = tableFrame;
        } completion: nil];
        self.tableView.scrollEnabled = shouldScroll;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([change[NSKeyValueChangeKindKey] unsignedIntValue] == NSKeyValueChangeSetting &&
        [keyPath isEqualToString:@"contentSize"] &&
        !CGSizeEqualToSize([change[NSKeyValueChangeOldKey] CGSizeValue], [change[NSKeyValueChangeNewKey] CGSizeValue])) {
        [self resizeTableAccordingToContentSize:[change[NSKeyValueChangeNewKey] CGSizeValue]];
    } 
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
    [self resizeTableAccordingToContentSize:self.tableView.contentSize]; }

- (void)dealloc {
    [self.tableView removeObserver:self forKeyPath:@"contentSize"];
}

Ответ 15

Решение Mu для этого в swift 3. Вызовите этот метод в viewDidAppear

func UITableView_Auto_Height(_ t : UITableView)
{
        var frame: CGRect = t.frame;
        frame.size.height = t.contentSize.height;
        t.frame = frame;        
}

Ответ 16

Задайте свойства вида таблицы для

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100

И в ячейке таблицы со numberOfLines = 0 вашего свойства метки numberOfLines = 0

установите верхние, нижние, левые и правые ограничения на верхнюю, нижнюю, левую и правую границы элементов. ** Он автоматически рассчитает высоту