Pergunta

Estou criando um aplicativo que terá uma pergunta em um UILabel E respostas de múltipla escolha exibidas em UITableView, cada linha mostrando uma escolha múltipla. Perguntas e respostas variam, então eu preciso disso UITableView ser dinâmico em altura.

Eu gostaria de encontrar um sizeToFit contorne para a mesa. Onde o quadro da tabela é definido para a altura de todo o seu conteúdo.

Alguém pode aconselhar sobre como eu posso conseguir isso?

Foi útil?

Solução

Na verdade, eu mesmo encontrei a resposta.

Eu apenas crio um novo CGRect para o tableView.frame com o height do table.contentSize.height

Que define a altura do UITableView para o height de seu conteúdo. Como o código modifica a interface do usuário, não se esqueça de executá -lo no tópico principal:

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;
    });

Outras dicas

Solução Swift 5 e 4.2 sem KVO, DispatchQueue ou definição de restrições você mesmo.

Esta solução é baseada em Resposta de Gulz.

1) Crie uma subclasse de 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) Adicione um UITableView ao seu layout e defina restrições por todos os lados. Defina a classe para ContentSizedTableView.

3) Você deve ver alguns erros, porque o storyboard não leva nossa subclasse ' intrinsicContentSize em conta. Corrija isso abrindo o inspetor de tamanho e substituindo o IntrinsicContentSize para um valor de espaço reservado. Esta é uma substituição para o tempo de design. Em tempo de execução, ele usará a substituição em nosso ContentSizedTableView classe


Atualizar: Código alterado para Swift 4.2. Se você estiver usando uma versão anterior, use UIViewNoIntrinsicMetric ao invés de UIView.noIntrinsicMetric

Solução rápida

Siga esses passos:

1- Defina a restrição de altura para a mesa do storyboard.

2- Arraste a restrição de altura do storyboard e crie @Iboutlet para ele no arquivo do View Controller.

    @IBOutlet weak var tableHeight: NSLayoutConstraint!

3- Então você pode alterar a altura da tabela dinâmica usando este código:

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

Atualizar

Se a última linha for cortada para você, tente ligar para o ViewWillLayoutSubViews () na função da célula de WillDisplay:

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

Eu tentei isso no iOS 7 e funcionou para mim

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

Adicione um observador para a propriedade Contentsize na visualização da tabela e ajuste o tamanho do quadro de acordo

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

Então, no retorno de chamada:

- (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;
    }

Espero que isso ajude você.

Caso você não queira rastrear as mudanças de tamanho de conteúdo da Table View, você pode achar essa subclasse útil.

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
    }
}

Eu tinha uma exibição de tabela dentro da exibição de rolagem e tive que calcular a altura do TableView e redimensioná -la de acordo. Essas são as etapas que tomei:

0) Adicione um UIView ao seu ScrollView (provavelmente funcionará sem esta etapa, mas eu o fiz para evitar possíveis conflitos) - essa será uma visualização Conterer para a visualização da sua tabela. Se você der essa etapa, defina as bordas das bordas para a TableView.

1) Crie uma subclasse do 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) Defina a classe de uma visualização de tabela no storyboard como intrinsictableView: captura de tela: http://joxi.ru/a2xeenPsybwq0a

3) Defina a altura da sua tabela View

4) Arraste o iboutlet da sua mesa para o seu ViewController

5) Arraste o iboutlet da restrição de altura da sua mesa para o seu ViewController

6) Adicione este método ao seu ViewController:

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

Espero que isto ajude

Swift 3, iOS 10.3

Solução 1:Apenas coloque self.tableview.sizeToFit() dentro cellForRowAt indexPath função. Certifique -se de definir a altura da tabela de tabela mais alta e precisar. Esta é uma boa solução se você não tiver visualizações abaixo da TableView. No entanto, se você tiver, a Restrição de Tabela de Tabela Inferior não será atualizada (eu não tentei corrigi -la porque criei a Solução 2)

Exemplo:

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()
}

Solução 2:Defina a restrição de altura da TabelawView no storyboard e arraste -a para o ViewController. Se você conhece a altura média do seu celular e sabe quantos elementos sua matriz contém, você pode fazer algo assim:

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

*EDITAR:

Depois de todas as soluções que tentei e todas estavam consertando algo, mas não completamente, isto é a resposta que explica e corrige esse problema completamente.

Caso o seu conteúdo não seja certo, isso é baseado no meio estimado (automático), use isso antes

tableView.estimatedRowHeight = 0;

fonte : https://forums.developer.apple.com/thread/81895

Resposta de Mimo e Resposta de Anooj VM Ambos são impressionantes, mas há um pequeno problema se você tiver uma lista grande, é possível que a altura do quadro seja interrompida algumas das suas células.

Então. Eu modifiquei a resposta um pouco:

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;
}

Existe uma maneira muito melhor de fazê -lo se você usar o AUTOLAYOUT: altere a restrição que determina a altura. Basta calcular a altura do conteúdo da sua tabela, depois encontre a restrição e altere -a. Aqui está um exemplo (assumindo que a restrição que determina a altura da sua tabela é realmente uma restrição de altura com relação "igual"):

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
            }
        }
    }
}

Como uma extensão da resposta de Anooj VM, sugiro o seguinte Atualize o tamanho do conteúdo apenas quando ele mudar.

Essa abordagem também Desative a rolagem corretamente e Suporte listas maiores e rotação. Não há necessidade de despachar_async porque as alterações do conteúdo são despachadas no encadeamento principal.

- (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"];
}

Versão Objc de 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;
}

Você pode experimentar este personalizado AGTableView

Para definir uma restrição de altura da TableView usando storyboard ou programaticamente. (Esta classe busca automaticamente uma restrição de altura e defina a altura da visualização de conteúdo na altura da sua altura da visualização).

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
                }
            }
        }
    }
}

Observação Se algum problema para definir uma altura, então yourTableView.layoutSubviews().

Com base na resposta de FL034. Mas pelo Xamarin.ios usuários:

    [Register("ContentSizedTableView")]
    public class ContentSizedTableView : UITableView
    {
        public ContentSizedTableView(IntPtr handle) : base(handle)
        {
        }

        public override CGSize ContentSize { get => base.ContentSize; set { base.ContentSize = value; InvalidateIntrinsicContentSize(); } }
        public override CGSize IntrinsicContentSize
        {
            get
            {
                this.LayoutIfNeeded();
                return new CGSize(width: NoIntrinsicMetric, height: ContentSize.Height);
            }
        }
    }

Se você deseja que sua tabela seja dinâmica, precisará usar uma solução com base no conteúdo da tabela, conforme detalhado acima. Se você simplesmente deseja exibir uma tabela menor, pode usar uma visualização de contêiner e incorporar um uabableViewController nela - o uabableView será redimensionado de acordo com o tamanho do contêiner.

Isso evita muitos cálculos e chamadas para o layout.

MU Solução para isso em Swift 3: Chame esse método em viewDidAppear

func UITableView_Auto_Height(_ t : UITableView)
{
        var frame: CGRect = t.frame;
        frame.size.height = t.contentSize.height;
        t.frame = frame;        
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top