A configuração de Altura da linha da célula personalizada no storyboard não está respondendo

213

Estou tentando ajustar a altura da célula para uma das células na minha exibição de tabela. Estou ajustando o tamanho da configuração "altura da linha" dentro do "inspetor de tamanho" da célula em questão. Quando executo o aplicativo no meu iPhone, a célula tem o tamanho padrão definido no "tamanho da linha" na exibição da tabela.

Se eu alterar o "tamanho da linha" da exibição da tabela, o tamanho de todas as células será alterado. Não quero fazer isso, pois quero um tamanho personalizado apenas para uma célula. Eu já vi muitas postagens com uma solução programática para o problema, mas eu preferiria fazê-lo através do storyboard, se isso for possível.

zirinisp
fonte

Respostas:

295

Nas células dinâmicas , o rowHeightconjunto no UITableView sempre substitui o rowHeight das células individuais.

Mas em células estáticas , o rowHeightconjunto de células individuais pode substituir os UITableView.

Não tem certeza se é um bug, a Apple pode estar fazendo isso intencionalmente?

pixelfreak
fonte
36
Resposta certa nº 3, e especificamente porque esta resposta se aplica ao Interface Builder / Storyboards. Se você selecionar a célula no IB, o Inspetor de Tamanho mostrará a Altura da Linha na parte superior (com uma caixa de seleção "personalizada"), mas se você selecionar a exibição inteira da tabela, o Inspetor de Tamanho também mostrará a Altura da Linha na parte superior (não "personalizado"). " nesse caso). Como diz pixelfreak, apenas a configuração de exibição de tabela é usada para células dinâmicas. (Não tenho certeza se é intencional)
ruibarbo
8
essa resposta implica que a solução seria simplesmente alterar o UITableViewconteúdo de Dynamic Prototypespara Static Cells, eu fiz isso e todo o meu projeto explodiu ... quase fui morto.
Abbood
4
Existe alguma maneira de recuperar esse número? A única maneira de cavar fundo é mergulhar no storyboard e retirá-lo de lá?
Biclops
Definitivamente NÃO é um bug, porque sua tabela é Dinâmica, então como o sistema pode saber onde você utilizou cada uma dessas células? E quantas vezes cada protótipo seria usado.
Vincent Bernier
Parece haver também um problema se você configurar inicialmente uma exibição de tabela como "células estáticas" e depois alterá-la para "protótipos dinâmicos". Eu tive um problema em que até o método delegado para rowHeight estava sendo ignorado. Eu o resolvi editando diretamente o XML do storyboard e, finalmente, reconstruí a cena do storyboard do zero, usando protótipos dinâmicos desde o início.
Eric Goldberg
84

Se você usar UITableViewController, implemente este método:

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

Na função de uma linha, você pode escolher Altura. Por exemplo,

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) {
       return 100;
    } 
    else {
       return 60;
    }
}

Neste exemplo, a altura da primeira linha é de 100 pixels e as demais são de 60 pixels.

Espero que este possa ajudá-lo.

Beber
fonte
2
Beber, SEMPRE é necessário fazer as duas coisas (marque Personalizar e edite o valor da Altura da linha no storyboard && especifique-o no heightForRowAtIndexPath)?
Marcelkoko
Você poderia me ajudar com isso? Eu preciso ter células dinâmicas com altura dinâmica, mas se eu usar esse método, não importa o que eu retorne, todas as células desaparecerão. Exceto no iPhone 5, o dispositivo , onde ele funciona como esperado. Você consegue entender o porquê?
Ostmeistro
34

Para células dinâmicas, rowHeightdefinir UITableViewsempre substitui as células individuais rowHeight.

Esse comportamento é, na IMO, um bug. Sempre que você precisar gerenciar sua interface do usuário em dois lugares, ela estará sujeita a erros. Por exemplo, se você alterar o tamanho da sua célula no storyboard, lembre-se de alterá-los heightForRowAtIndexPath:também. Até a Apple corrigir o erro, a melhor solução atual é substituir heightForRowAtIndexPath:, mas use as células de protótipo reais do storyboard para determinar a altura em vez de usar números mágicos . Aqui está um exemplo:

- (CGFloat)tableView:(UITableView *)tableView 
           heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /* In this example, there is a different cell for
       the top, middle and bottom rows of the tableView.
       Each type of cell has a different height.
       self.model contains the data for the tableview 
    */
    static NSString *CellIdentifier;
    if (indexPath.row == 0) 
        CellIdentifier = @"CellTop";
    else if (indexPath.row + 1 == [self.model count] )
        CellIdentifier = @"CellBottom";
    else
        CellIdentifier = @"CellMiddle";

    UITableViewCell *cell = 
              [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    return cell.bounds.size.height;
}

Isso garantirá que quaisquer alterações nas alturas das células do seu protótipo sejam selecionadas automaticamente no tempo de execução e você só precisará gerenciar sua interface do usuário em um só lugar: o storyboard.

memmons
fonte
2
Publiquei uma resposta com o código completo, incluindo o cache; consulte stackoverflow.com/a/16881312/292166 #
3100 JosephH
1
Uau uau uau! Eu votei na resposta, mas tenha consciência! Esse método causa não apenas uma penalidade de desempenho, como disse @Brennan nos comentários, mas também causa uma crescente alocação de memória a cada reloadData, algo como vazamento de memória! É necessário usar a solução alternativa do lensovet acima! Passe um dia para pegar esse vazamento de memória!
skywinder
Não funciona no Swift, porque cell.bounds.size.height sempre retorna 0.0
King-Wizard
1
As células ainda não existem no momento em que o sistema chamou seu método tableView: heightForRowAtIndexPath. Você deve poder usar o indexPath que é passado para você indexar em seu modelo de dados e ver que tipo de célula será usada lá, e calcular a altura dessa célula e devolvê-la. Isso permite que o sistema coloque as células na tabela antes de criar qualquer célula.
King-Wizard
1
Além disso, você não deve chamar seu próprio método cellForRowAtIndexPath. As exibições de tabela possuem um método cellForRowAtIndexPath que retornará uma célula para esse caminho de índice, se estiver atualmente visível na tela, mas frequentemente esse caminho de índice não possui uma célula associada.
King-Wizard
22

Eu construí o código com as várias respostas / comentários para que isso funcione para storyboards que usam células protótipo.

Este código:

  • Não requer que a altura da célula seja definida em outro lugar que não seja o local óbvio no storyboard
  • Caches a altura por motivos de desempenho
  • Usa uma função comum para obter o identificador de célula para um caminho de índice para evitar lógica duplicada

Obrigado ao Answerbot, Brennan e lensovet.

- (NSString *)cellIdentifierForIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = nil;

    switch (indexPath.section)
    {
        case 0:
            cellIdentifier = @"ArtworkCell";
            break;
         <... and so on ...>
    }

    return cellIdentifier;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];
    static NSMutableDictionary *heightCache;
    if (!heightCache)
        heightCache = [[NSMutableDictionary alloc] init];
    NSNumber *cachedHeight = heightCache[cellIdentifier];
    if (cachedHeight)
        return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    <... configure cell as usual...>
JosephH
fonte
Eu sei que esta é uma resposta antiga, mas alguém acha que isso resulta em um estouro de pilha? Quando a exibição é carregada, eu recebo UISectionRowData refreshWithSection: tableViewRowData: que chama tableView: heightForRowAtIndexPath: que chama dequeueReusableCellWithIdentifier: que chama [UISectionRowData ...] para que eu tenha um estouro de pilha recursivo. Eu realmente queria usar esta solução, mas ela não parece funcionar com o iOS7.
Sbaker 16/05
Não funciona no Swift, porque cell.bounds.size.height sempre retorna 0,0
King-Wizard
As células ainda não existem no momento em que o sistema chamou seu método tableView: heightForRowAtIndexPath. Você deve poder usar o indexPath que é passado para você indexar em seu modelo de dados e ver que tipo de célula será usada lá, e calcular a altura dessa célula e devolvê-la. Isso permite que o sistema coloque as células na tabela antes de criar qualquer célula.
King-Wizard
Além disso, você não deve chamar seu próprio método cellForRowAtIndexPath. As exibições de tabela possuem um método cellForRowAtIndexPath que retornará uma célula para esse caminho de índice, se estiver atualmente visível na tela, mas frequentemente esse caminho de índice não possui uma célula associada.
King-Wizard
@ Snow-Tiger Não tentei isso rapidamente, mas não entendo seus comentários. Eu não 'chamo [meu] próprio método cellForRowAtIndexPath' e é deliberado que não o faço. Também estou ciente de que as células não existem na tabela de tempoView: heightForRowAtIndexPath é chamada, esse é o ponto principal deste código e por que ele usa dequeueReusableCellWithIdentifier para obter uma célula. O código publicado nesta resposta funciona. Ou há algo extra acontecendo rapidamente, há algo errado na conversão rápida ou está tentando resolver um problema diferente que essa pergunta / resposta visa.
Josephh
19

Na verdade, existem dois lugares em que você precisa alterar a altura da linha, primeiro a célula (você já a alterou) e agora selecione a Visualização da tabela e verifique o Inspetor de tamanho

Kristjan H.
fonte
no tableview (não cellview)> na guia inspetor de tamanho> altura da linha
iman kazemayni
Me deixa louco cada vez que esqueço esse detalhe. Não faço ideia por que o ajuste da célula não atualiza o tableView automaticamente ao usar storyboards!
Scooter
11

Eu acho que é um bug.

Tente ajustar a altura não pelo inspetor do Utility, mas pelo mouse diretamente no storyboard.

Eu resolvi esse problema com esse método.

Fogni
fonte
10

Se você estiver usando rápido, use assim. Não use o storyboard para selecionar a altura da linha. Defina programaticamente a altura da linha da tabela assim,

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    if indexPath.row == 0 || indexPath.row == 1{
        let cell = self.tableView.dequeueReusableCellWithIdentifier("section1", forIndexPath: indexPath) as! Section1TableViewCell
        self.tableView.rowHeight = 150
        cell.label1.text = "hiiiiii"
        cell.label2.text = "Huiiilllllll"
        return cell

    } else {

        let cell = self.tableView.dequeueReusableCellWithIdentifier("section2", forIndexPath: indexPath) as! Section2TableViewCell
        self.tableView.rowHeight = 60
        cell.label3.text = "llll"
        return cell
    }

}
Chathuranga Silva
fonte
Isso funciona, mas geralmente você pode fazer:cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height
Andy Chou
7

Você pode obter a altura do UITableviewCell (no UITableviewController - células estáticas) no storyboard com a ajuda das seguintes linhas.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
   CGFloat height = [super tableView:tableView heightForRowAtIndexPath:indexPath];

    return height;
}
Hiren Panchal
fonte
6

Abra o storyboard na visualização XML e tente editar o rowHeightatributo do elemento desejado.

Funcionou para mim quando tentei definir rowHeight personalizado para minha linha com protótipo. Não está funcionando via inspetor, mas via XML funciona.

Shtirlic
fonte
1
Cliquei com o botão direito do mouse e selecionei "abrir como" -> "código fonte". O rowHeight já estava definido como 120. Acredito que o storyboard tenha um bug e ignore a altura da célula personalizada.
Zirinisp 10/01/12
6

Você pode usar o protótipo cellscom um costume heighte, em seguida, chamar cellForRowAtIndexPath:e retornar seu frame.height.:.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView
                    cellForRowAtIndexPath:indexPath];
    return cell.frame.size.height;
}
Henrik Hartz
fonte
A adição desse método funcionou muito bem e agora mantém toda a personalização no storyboard, como deve pertencer.
Daspianist
De longe, a melhor solução
TheJeff
4

Se você deseja definir uma altura de linha estática, pode fazer algo assim:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 120;
}
Victor Palhares
fonte
4

Eu tenho lutado recentemente com isso. Meu problema foi que as soluções postadas acima usando o heightForRowAtIndexPath:método funcionariam para o iOS 7.1 no Simulador, mas depois estragaram completamente os resultados simplesmente mudando para o iOS 8.1.

Comecei a ler mais sobre células de auto-dimensionamento (introduzidas no iOS 8, leia aqui ). Era evidente que o uso de UITableViewAutomaticDimensionajudaria no iOS 8. Tentei usar essa técnica e excluí o uso de heightForRowAtIndexPath:e voila, estava funcionando perfeitamente no iOS 8 agora. Mas o iOS 7 não estava. O que eu devo fazer? Eu precisava heightForRowAtIndexPath:do iOS 7 e não do iOS 8.

Aqui está a minha solução (aparada por uma questão de brevidade) que empresta da resposta @ JosephphH postada acima:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.estimatedRowHeight = 50.;
    self.tableView.rowHeight = UITableViewAutomaticDimension;

    // ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
        return UITableViewAutomaticDimension;

    } else {
        NSString *cellIdentifier = [self reuseIdentifierForCellAtIndexPath:indexPath];
        static NSMutableDictionary *heightCache;
        if (!heightCache)
            heightCache = [[NSMutableDictionary alloc] init];
        NSNumber *cachedHeight = heightCache[cellIdentifier];
        if (cachedHeight)
            return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
    }
}

- (NSString *)reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath {
    NSString * reuseIdentifier;
    switch (indexPath.row) {
        case 0:
            reuseIdentifier = EventTitleCellIdentifier;
            break;
        case 2:
            reuseIdentifier = EventDateTimeCellIdentifier;
            break;
        case 4:
            reuseIdentifier = EventContactsCellIdentifier;
            break;
        case 6:
            reuseIdentifier = EventLocationCellIdentifier;
            break;
        case 8:
            reuseIdentifier = NotesCellIdentifier;
            break;
        default:
            reuseIdentifier = SeparatorCellIdentifier;
            break;
    }

    return reuseIdentifier;
}

SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO (@ "8.0") é realmente de um conjunto de definições de macro que estou usando e que encontrei em algum lugar (muito útil). Eles são definidos como:

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
speby
fonte
2

O mesmo problema ocorreu ao trabalhar no XCode 9 usando o Swift 4 .

Adicione AutoLayout para os elementos da interface do usuário dentro da célula e a altura da linha da célula personalizada funcionará adequadamente, conforme especificado.

Murari Varma
fonte
1
Oi, por favor, como você fez isso?
Back Packer
Você só precisa adicionar uma restrição definida na parte inferior da célula.
Justin
Quando faço isso e seleciono "automático" no storyboard para a altura da célula, ele deseja definir todas as alturas para 44 no storyboard, embora pareça correto quando eu corro. Como posso exibir o storyboard corretamente?
David
1

Para células dinâmicas, o rowHeight definido no UITableView sempre substitui o rowHeight das células individuais. Apenas calcule a altura dinâmica se o conteúdo estiver dentro da linha.

Aks
fonte
0

A única solução real que pude encontrar é esta

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = ...; // Instantiate with a "common" method you'll use again in cellForRowAtIndexPath:
    return cell.frame.size.height;
}

Isso funciona e permite não ter uma opção horrível / se duplicar a lógica já no StoryBoard. Não tenho certeza sobre o desempenho, mas acho que ao chegar na cellForRow:célula já sendo inicializado, é tão rápido. É claro que provavelmente há danos colaterais aqui, mas parece que funciona bem para mim aqui.

Também publiquei isso aqui: https://devforums.apple.com/message/772464

Edição: Ortwin Gentz me lembrou que heightForRowAtIndexPath:será chamado para todas as células do TableView, não apenas as visíveis. Parece lógico, já que o iOS precisa saber a altura total para poder mostrar as barras de rolagem corretas. Isso significa que provavelmente está bem em TableViews pequenos (como 20 células), mas esqueça isso em um TableView de 1000 células.

Além disso, o truque anterior com XML: O mesmo que o primeiro comentário para mim. O valor correto já estava lá.

StuFF mc
fonte
0

Adicionado como comentário, mas postando como resposta para visibilidade:

Parece haver também um problema se você configurar inicialmente uma exibição de tabela como "células estáticas" e alterá-la para "protótipos dinâmicos". Eu tive um problema em que até o método delegado heightForRowAtIndexPathestava sendo ignorado. Eu o resolvi editando diretamente o XML do storyboard, e finalmente reconstruí a cena do storyboard do zero, usando protótipos dinâmicos desde o início.

Eric Goldberg
fonte
0

Como não encontrei nenhuma solução para esse problema através do Interface Builder, decidi postar uma solução programática para o problema no Swift usando duas células dinâmicas , mesmo que a pergunta inicial pedisse uma solução através do Interface Builder. Independentemente, acho que poderia ser útil para a comunidade Stack Overflow:

    import UIKit

    enum SignInUpMenuTableViewControllerCellIdentifier: String {
       case BigButtonCell = "BigButtonCell"
       case LabelCell = "LabelCell"
    }

    class SignInUpMenuTableViewController: UITableViewController {
            let heightCache = [SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell : CGFloat(50),
                              SignInUpMenuTableViewControllerCellIdentifier.LabelCell : CGFloat(115)]

    private func cellIdentifierForIndexPath(indexPath: NSIndexPath) -> SignInUpMenuTableViewControllerCellIdentifier {
        if indexPath.row == 2 {
            return SignInUpMenuTableViewControllerCellIdentifier.LabelCell
        } else {
            return SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell
        }
    }

   override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
       return self.heightCache[self.cellIdentifierForIndexPath(indexPath)]!
   }

   ...

  }
O rei da bruxaria
fonte
Isto é tudo muito bem e bom, mas a menos que eu estou interpretando mal o código, ele simplesmente se baseia em números mágicos e ignora completamente o valor que você definiu no IB para as células dinâmicas Então ... não exatamente uma solução para questão do OP
Danny
0

Outra coisa que você pode fazer é acessar o Esboço do documento, selecionar a visualização da tabela em que a célula do seu protótipo está aninhada. Em seguida, no Inspetor de tamanhos, altere a altura da linha da visualização da tabela para o valor desejado e desmarque a caixa Automático.

Amin Rezapour
fonte