UITableview: Como desativar a seleção para algumas linhas, mas não para outras

298

Estou exibindo em um tableviewconteúdo de grupo analisado de XML. Desejo desativar o evento click nele (não devo clicar nele). A tabela contém dois grupos. Desejo desativar a seleção apenas para o primeiro grupo, mas não para o segundo. Clicando na primeira linha do segundo grupo navigatesno meu tube player view.

Como posso selecionar apenas grupos ou linhas específicos?

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    if(indexPath.section!=0)
    if(indexPath.row==0)    

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:tubeUrl]];   
}

Obrigado.

Guerreiro
fonte

Respostas:

499

Você só precisa colocar esse código em cellForRowAtIndexPath

Para desativar a propriedade de seleção da célula: (ao tocar na célula)

cell.selectionStyle = UITableViewCellSelectionStyleNone;

Para permitir poder selecionar (toque) na célula: (tocando na célula)

// Default style
cell.selectionStyle = UITableViewCellSelectionStyleBlue;

// Gray style
cell.selectionStyle = UITableViewCellSelectionStyleGray;

Observe que uma célula com selectionStyle = UITableViewCellSelectionStyleNone;ainda fará com que a interface do usuário chame didSelectRowAtIndexPathquando tocada pelo usuário. Para evitar isso, faça o sugerido abaixo e defina.

cell.userInteractionEnabled = NO;

em vez de. Observe também que você pode definir cell.textLabel.enabled = NO;para esmaecer o item.

Pugal
fonte
1
Este método irá gerar um erro se você tentar deslizar para excluir.
Vive
116
Isso é hack! Faça isso, use a resposta abaixo - usando willSelectRowAtIndexPath com retorno nulo!
22412 Peter Stajger
4
O material userInteractionEnabled não está correto, o gancho willSelectRowAtIndexPath, como as pessoas observam.
Jonny
23
No iOS 6.0 e posterior, tableView:shouldHighlightRowAtIndexPath:é um novo UITableViewDelegatemétodo que retorna um com BOOLbase em se o passado indexPathdeve ou não ser destacado. Se você está construindo para a versão 6.0 e posterior, recomendo fortemente esta nova API.
Cbowns
3
se você estiver fazendo isso no Swift e tiver problemas, mas acabou aqui, precisará definir cell!.selectionStyle = .Nonequal força desembrulha a célula, permitindo acessar suas propriedades.
Marcos
371

Se você deseja tornar uma linha (ou subconjunto de linhas) não selecionável , implemente o UITableViewDelegatemétodo -tableView:willSelectRowAtIndexPath:(também mencionado pelo TechZen). Se indexPathnão puder ser selecionado , retorne nil, caso contrário, retorne indexPath. Para obter o comportamento de seleção padrão, basta retornar o indexPathpassado ao seu delegatemétodo, mas você também pode alterar a seleção de linha retornando um diferente indexPath.

exemplo:

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // rows in section 0 should not be selectable
    if ( indexPath.section == 0 ) return nil;

    // first 3 rows in any section should not be selectable
    if ( indexPath.row <= 2 ) return nil;

    // By default, allow row to be selected
    return indexPath;
}
Brian Chapados
fonte
2
Pequenos detalhes: você menta <= 2, não = <2. Bad smiley! :)
Jonas Byström
23
Gostaria também de acrescentar que esta é a resposta correta, mas você também deve definir o selectionStyle como UITableViewCellSelectionStyleNone em cellForRowAtIndexPath. Por quê? porque sem esse estilo de seleção, a célula não pode ser selecionada, mas ainda será exibida "como selecionada" quando tocada (para que ela mude para a cor selecionada, se houver uma para as outras células)
TooManyEduardos
4
isso deve ter mais
votos positivos
2
Acho que a principal razão pela qual essa não é a resposta aceita é porque a resposta aceita é mais fácil. Embora não seja a 'melhor maneira' de fazer isso, porque sua tabela ainda chamará setSelected: em suas células, ela ainda funciona visualmente. Esta resposta aqui exige mais trabalho, mas é a maneira correta de fazê-lo (você também deve combinar a sugestão @TooManyEduardos) para evitar consequências imprevistas da tabela que chama setSelected: em suas células.
Brian Sachetta
Eu gosto desta resposta, mas se você não quer duplicar a sua lógica em cellForRowAtIndexPath e willSelectRowAtIndexPath, basta verificar se cell.selectionStyle == nenhum em willSelectRowAtIndexPath a zero de retorno (ver minha resposta abaixo)
Eric D'Souza
61

A partir do iOS 6, você pode usar

-tableView:shouldHighlightRowAtIndexPath:

Se você retornar NO, desabilitará o destaque da seleção e os seguimentos acionados pelo storyboard conectados a essa célula.

O método é chamado quando um toque é pressionado em uma linha. Retornar NOa essa mensagem interrompe o processo de seleção e não faz com que a linha atualmente selecionada perca a aparência selecionada enquanto o toque é pressionado.

Referência do protocolo UITableViewDelegate

Keller
fonte
3
Acordado. Para iOS 7 & acima este deve ser o método preferido (na verdade no iOS 8 outros métodos não estavam a funcionar para mim.)
race_carr
Essa deve ser a resposta principal.
Cameron E
16

Nenhuma das respostas acima realmente resolve o problema corretamente. O motivo é que queremos desativar a seleção da célula, mas não necessariamente de subvisões dentro da célula.

No meu caso, eu estava apresentando um UISwitch no meio da linha e queria desativar a seleção para o restante da linha (que está vazia), mas não para o comutador! A maneira correta de fazer isso está no método

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

onde uma declaração do formulário

[cell setSelectionStyle:UITableViewCellSelectionStyleNone];

desabilita a seleção para a célula específica e, ao mesmo tempo, permite ao usuário manipular a opção e, portanto, usar o seletor apropriado. Isso não é verdade se alguém desabilitar a interação do usuário através do

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

método que apenas prepara a célula e não permite a interação com o UISwitch.

Além disso, usando o método

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

para desmarcar a célula com uma declaração do formulário

[tableView deselectRowAtIndexPath:indexPath animated:NO];

ainda mostra a linha sendo selecionada enquanto o usuário pressiona o contentView original da célula.

Apenas meus dois centavos. Tenho certeza de que muitos acharão isso útil.

Rato poderoso
fonte
Aceita. E a devolução nula willSelectRowAtIndexPathtem o mesmo problema, as subvisões não são selecionáveis.
precisa saber é
Observe também que, se você tiver uma tabela estática, poderá definir a Seleção como Nenhuma no Construtor de Interface para as células que não deseja que a seleção apareça.
precisa saber é
11

Você intercepta seleções com esses métodos de fonte de dados.

 tableView:willSelectRowAtIndexPath: 
 tableView:didSelectRowAtIndexPath: 
 tableView:willDeselectRowAtIndexPath: 
 tableView:didDeselectRowAtIndexPath:

Nesses métodos, você verifica se a linha selecionada é aquela que deseja ser selecionável. Se for, execute uma ação, se não, não faça nada.

Infelizmente, você não pode desativar a seleção em apenas uma seção. É a mesa inteira ou nada.

No entanto, você pode definir a selectionStylepropriedade de células da tabela como UITableViewCellSelectionStyleNone. Acredito que tornará a seleção invisível. Combinado com os métodos acima, que devem fazer com que as células pareçam completamente inertes da perspectiva do usuário.

Edit01:

Se você possui uma tabela na qual apenas algumas das linhas são selecionáveis, é importante que as células das linhas selecionáveis ​​sejam visualmente distintas das linhas não selecionáveis. O botão de acessórios da chevron é a maneira padrão de fazer isso.

Seja como for, você não quer que seus usuários tentem selecionar linhas e pensem que o aplicativo falhou porque a linha não faz nada.

TechZen
fonte
por favor, descreva como usar esses métodos ou dar alguns links programa de amostra
Guerreiro
2
@Warrior: Se você instalou o SDK, pode abrir o Xcode, vá para Help -> Developer documentatione copie esses nomes de métodos no campo de pesquisa para ver como ele funciona. A documentação do desenvolvedor também contém código de exemplo.
Kennytm
10

Para o Swift 4.0, por exemplo:

cell.isUserInteractionEnabled = false
cell.contentView.alpha = 0.5
Alessandro Ornano
fonte
9

Você precisa fazer algo como o seguinte para desativar a seleção de células dentro do cellForRowAtIndexPathmétodo:

[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell setUserInteractionEnabled:NO];

Para mostrar a célula acinzentada, coloque o seguinte no tableView:WillDisplayCell:forRowAtIndexPathmétodo:

[cell setAlpha:0.5];

Um método permite controlar a interatividade, o outro permite controlar a aparência da interface do usuário.

azfar
fonte
5

Para interromper a seleção de algumas células, use:

cell.userInteractionEnabled = NO;

Além de impedir a seleção, isso também impede que tableView: didSelectRowAtIndexPath: seja chamado pelas células que o definiram.

JosephH
fonte
1
Uma coisa estranha acontece o tempo todo: quando eu defino userInteractionEnabledcomo NO em uma célula (mais claramente, um indexPath), a interação do usuário com algumas OUTRAS células é desativada. Não faço idéia por que isso acontece toda vez que uso userInteractionEnabledem células de exibição de tabela. É um bug conhecido ou eu fiz algo errado?
precisa saber é o seguinte
3
@ Philip007 As células "NÃO" estão sendo reutilizadas? Nesse caso, pode ser necessário defini-lo como "SIM" quando desenfileirar uma célula.
precisa saber é
1
Por que configurá-lo para "SIM"? Quero que para ser NO (não permitir a interacção do utilizador) .. Um exemplo típico é aquele que definido de interacção com o utilizador na primeira linha ( if [indexPath row] == 0) para NOem tableView:cellForRowAtIndexPath:após dequeueing da célula. Geralmente funciona bem no início. Depois de várias chamadas reloadData, de repente a quinta linha não permite a interação e a quarta linha. Não consigo descobrir quando isso acontecerá. A coisa toda parece aleatória.
precisa saber é o seguinte
6
@ Philip007 Se você estiver reutilizando células, você precisará redefini-la para "SIM" para as células que você NÃO quer ser interativo. Caso contrário, se uma célula "NÃO" for reutilizada em uma linha que você deseja que seja interativa, ela ainda será desativada. ou seja:if [indexPath row] == 0) { set to NO } else { set to YES }
JosephH
5

Você pode usar o tableView:willDisplayCellmétodo para fazer todos os tipos de personalização em um tableViewCell.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
     [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
     [cell setUserInteractionEnabled:NO];

     if (indexPath.section == 1 && indexPath.row == 0)
     {
         [cell setSelectionStyle:UITableViewCellSelectionStyleGray];
         [cell setUserInteractionEnabled:YES];
     }
} 

Nesse código acima, o usuário pode selecionar apenas a primeira linha na segunda seção do tableView. O restante de todas as linhas não pode ser selecionado. Obrigado!

Abdul Rahman
fonte
5

Para rápida:

cell.selectionStyle = .None
cell.userInteractionEnabled = false
Esqarrouth
fonte
5

Minha solução baseada no modelo de dados:

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    let rowDetails = viewModel.rowDetails(forIndexPath: indexPath)
    return rowDetails.enabled ? indexPath : nil
}

func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
    let rowDetails = viewModel.rowDetails(forIndexPath: indexPath)
    return rowDetails.enabled
}
SoftDesigner
fonte
5

Para o Swift 4.0, isso fará o truque. Ele desativará a célula no método didSelectRowAtIndexPath, mas manterá as subvisões clicáveis.

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
         if (indexPath.row == clickableIndex ) { 
            return indexPath
         }else{
            return nil
        }
    }
Nilesh
fonte
Ou, resumindo return indexPath.row == clickableIndex ? indexPath : nil:, mantém seu código um pouco mais limpo ;-)
Mark
1

Use isso para fazer com que a célula pareça desabilitada e não selecionável:

cell.selectionStyle = UITableViewCellSelectionStyleNone;

Importante: observe que essa é apenas uma propriedade de estilo e, na verdade, não desativa a célula. Para fazer isso, você deve verificar selectionStylea didSelectRowAtIndexPath:implementação do seu delegado:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if(cell.selectionStyle == UITableViewCellSelectionStyleNone) {
        return;
    }

    // do your cell selection handling here
}
pt2ph8
fonte
1

Eu gosto da resposta de Brian Chapados acima. No entanto, isso significa que você pode ter a lógica duplicada no cellForRowAtIndexPath e depois no willSelectRowAtIndexPath, que pode facilmente ficar fora de sincronia. Em vez de duplicar a lógica, basta verificar o selectionStyle:

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (cell.selectionStyle == UITableViewCellSelectionStyleNone)
        return nil;

    else
        return indexPath;
}
Eric D'Souza
fonte
1

Achei isso útil, pois trabalha com tabelas estáticas e dinâmicas. Defino apenas o indicador de divulgação nas células que desejo permitir a seleção.

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (cell.accessoryType != UITableViewCellAccessoryDisclosureIndicator) {
        return nil;
    }
    return indexPath;
}
Jeffery Jones
fonte
1

no Xcode 7 sem codificação, você pode simplesmente fazer o seguinte:

Na exibição de estrutura de tópicos, selecione Célula de exibição de tabela. (A célula está aninhada em Cena do controlador de exibição de tabela> Controlador de exibição de tabela> Exibição de tabela. Talvez seja necessário divulgar esses objetos para ver a célula de exibição de tabela)

No inspetor Atributos, localize o campo Seleção e selecione Nenhum. Atribuir inspetor Com esta opção, a célula não receberá um destaque visual quando um usuário toca nela.

Tung Fam
fonte
0

SIMPLES

Basta usar cell.userInteractionEnabled = YES;a célula se ela puder navegar e de cell.userInteractionEnabled = NO;outra forma

Rick Royd Aban
fonte
1
Isso também impede a interação com qualquer visualização acessória dentro da célula, que pode não ser o comportamento desejado.
Greg Brown
0

Implemente apenas o método tableView:willSelectRowAtIndexPath: na fonte de dados para sua tabela. Se você deseja destacar a linha no caminho, retorne o indexPath fornecido. Caso contrário, retorne zero.

Exemplo do meu aplicativo:

- (NSIndexPath *)tableView:(UITableView *)tableView
    willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MySelectableObj* obj = [self objectAtPath:indexPath];
    if(obj==nil) return nil;
    return indexPath;
}

O bom disso é que shouldPerformSegueWithIdentifier:sender:não será chamado se o método acima retornar nulo, embora eu repita o teste acima apenas por completo.


fonte
0

Para o Xcode 6.3:

 cell.selectionStyle = UITableViewCellSelectionStyle.None;
uplearnedu.com
fonte
0

Eu concordo com a resposta de Bryan

se eu fizer

cell.isUserInteractionEnabled = false 

as subvisões dentro da célula não serão interagidas pelo usuário.

No outro site, definindo

cell.selectionStyle = .none

acionará o método didSelect, apesar de não atualizar a cor da seleção.

Usar willSelectRowAt é a maneira que resolvi meu problema. Exemplo:

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {

    if indexPath.section == 0{

        if indexPath.row == 0{
            return nil
        }

    }
    else if indexPath.section == 1{

        if indexPath.row == 0{
            return nil
        }

    }

    return indexPath

}
Reimond Hill
fonte
0

além de tableView.allowsMultipleSelection = truevocê precisará desmarcar o restante das células da seção ao selecionar:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if indexPath.section does not support multiple selection {
        // deselect the rest of cells
        tableView
            .indexPathsForSelectedRows?.compactMap { $0 }
            .filter { $0.section == indexPath.section && $0.row != indexPath.row }
            .forEach { tableView.deselectRow(at: $0, animated: true) }
    }
}
rgkobashi
fonte
-1

para o swift 3.0, você pode usar o código abaixo para desativar a interação do usuário com a célula UITableView

 cell.isUserInteractionEnabled = false

fonte