A subvisão UITableViewCell desaparece quando a célula é selecionada


178

Estou implementando uma exibição de tabela de seleção de cores em que o usuário pode selecionar, por exemplo, 10 cores (depende do produto). O usuário também pode selecionar outras opções (como capacidade do disco rígido, ...).

Todas as opções de cores estão dentro de sua própria seção de tableview.

Quero exibir um pequeno quadrado à esquerda do textLabel mostrando a cor real.

No momento, estou adicionando um UIView quadrado simples, fornecendo a cor de fundo correta, desta forma:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RMProductAttributesCellID];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:RMProductAttributesCellID] autorelease];
        cell.indentationWidth = 44 - 8;

        UIView *colorThumb = [[[UIView alloc] initWithFrame:CGRectMake(8, 8, 28, 28)] autorelease];
        colorThumb.tag = RMProductAttributesCellColorThumbTag;
        colorThumb.hidden = YES;
        [cell.contentView addSubview:colorThumb];
    }

    RMProductAttribute *attr = (RMProductAttribute *)[_product.attributes objectAtIndex:indexPath.section];
    RMProductAttributeValue *value = (RMProductAttributeValue *)[attr.values objectAtIndex:indexPath.row];
    cell.textLabel.text = value.name;
    cell.textLabel.backgroundColor = [UIColor clearColor];

    UIView *colorThumb = [cell viewWithTag:RMProductAttributesCellColorThumbTag];
    colorThumb.hidden = !attr.isColor;
    cell.indentationLevel = (attr.isColor ? 1 : 0);

    if (attr.isColor) {
        colorThumb.layer.cornerRadius = 6.0;
        colorThumb.backgroundColor = value.color;
    }

    [self updateCell:cell atIndexPath:indexPath];

    return cell;
}

Isso é exibido sem problemas.

Meu único problema é que, quando seleciono uma linha "colorida", durante a animação de seleção de desbotamento para azul, meu UIView personalizado (colorThumb) fica oculto. Torna-se visível novamente logo após o término da animação de seleção / desmarcação, mas isso produz um artefato feio.

O que devo fazer para corrigir isso? Não insiro a subvisão no lugar certo?

(Não há nada de especial no didSelectRowAtIndexPath, basta alterar o acessório da célula para uma caixa de seleção ou nada e desmarcar o indexPath atual).


O que é o upadteCell?
Idan

O updateCell faz alguns pequenos ajustes, como definir ou não a marca de seleção, escolher a cor do texto dependendo da disponibilidade, ... mas nenhuma alteração real relacionada à própria célula ou ao colorThumb.
Cyrille

a resposta aceita não fornece a solução, ver minha resposta abaixo para solução
Pavel Gurov

Respostas:


227

UITableViewCellmuda a cor de todos os pontos de vista sub fundo quando a célula é selecionada ou destacado, você pode resolver este problema, substituindo célula Tableview setSelected:animatede setHighlighted:animatede redefinir cor fundo vista.

No objetivo C:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
   UIColor *color = self.yourView.backgroundColor;        
   [super setSelected:selected animated:animated];

    if (selected){
        self.yourView.backgroundColor = color;
    }
}

-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated{
    UIColor *color = self.yourView.backgroundColor;        
    [super setHighlighted:highlighted animated:animated];

    if (highlighted){
        self.yourView.backgroundColor = color;
    }
}

No Swift 3.1:

override func setSelected(_ selected: Bool, animated: Bool) {
    let color = yourView.backgroundColor         
    super.setSelected(selected, animated: animated)

    if selected {
        yourView.backgroundColor = color
    }
}

override func setHighlighted(_ highlighted: Bool, animated: Bool) {
    let color = yourView.backgroundColor
    super.setHighlighted(highlighted, animated: animated)

    if highlighted {
        yourView.backgroundColor = color
    }
}

Precisamos das condições if (highlighted)e if (selected)? Eu acho que vai funcionar se não tivermos essas condições.
Rishabh Tayal

@RishabhTayal é, basicamente, para evitar a substituição da variável com o mesmo valor
cameloper

1
Lembre-se de que, quando você altera a cor do plano de fundo enquanto o item é selecionado, a cor antiga (incorreta) pode ser restaurada quando o item fica desmarcado. Se isso puder acontecer, remova as condições se (destacadas) e se (selecionadas).
Marcel W

Essa abordagem cancela a animação, por isso não faz sentido. Melhor definir o estilo de seleção de célula para .none.
Alexander Danilov

122

É porque a célula de exibição de tabela altera automaticamente a cor de plano de fundo de todas as visualizações na exibição de conteúdo para o estado destacado. Você pode considerar a subclasse UIViewpara desenhar sua cor ou usar uma UIImageViewimagem esticada personalizada de 1x1 px.


1
Me engane. Obviamente, as subvisões precisam ser transparentes para que a animação da seleção possa ocorrer corretamente. Obrigado!
Cyrille

52
Ou você pode re-definir a cor de fundo substituindo setHighlighted:animated:esetSelected:animated:
pt2ph8

1
De alguma forma, redefinir a cor de fundo não funcionou para mim (executando no iOS 8.1). Em vez disso, resolvi isso subclassificando minha visualização e substituindo setBackgroundColor como [super setBackgroundColor: [UIColor whiteColor]].
bizz84

44

Encontrei uma solução bastante elegante em vez de mexer nos métodos de seleção / destaque tableViewCell. Você pode criar uma subclasse do UIView que ignore a configuração da cor do plano de fundo para limpar a cor.

Swift 3/4:

class NeverClearView: UIView {
    override var backgroundColor: UIColor? {
        didSet {
            if backgroundColor != nil && backgroundColor!.cgColor.alpha == 0 {
                backgroundColor = oldValue
            }
        }
    }
}

Swift 2:

class NeverClearView: UIView {
    override var backgroundColor: UIColor? {
        didSet {
            if CGColorGetAlpha(backgroundColor!.CGColor) != 0 {
                backgroundColor = oldValue
            }
        }
    }
}

Versão Obj-C:

@interface NeverClearView : UIView

@end

@implementation NeverClearView

- (void)setBackgroundColor:(UIColor *)backgroundColor {
    if (CGColorGetAlpha(backgroundColor.CGColor) != 0) {
        [super setBackgroundColor:backgroundColor];
    }
}

@end

Isso é adorável. Facilmente a melhor solução se você tiver uma visão reutilizável, como um "crachá" ou "etiqueta" que nunca deve ter um plano de fundo claro. :: rant :: que solução confusa @UIKit, configurando todas as visualizações filho para transparentes quando você faz uma seleção de célula. Pelo menos limite às visualizações filho que têm a altura ou largura total da célula ou aquelas com profundidade de N.
SimplGy 11/05

@SimplGy A profundidade de destacar seria de fato uma opção doce, mas hey - este é UIKit, tenho visto coisas muito piores do que isso =)
Pavel Gurov

3
Funcionou para mim depois que eu mudei o que para CGColorGetAlpha (backgroundColor .CGColor!) == 0, uma vez que não era igual ao clearColor
piltdownman7

9

Outra maneira de gerenciar o problema é preencher a visualização com gradiente de gráficos principais, como:

CAGradientLayer* gr = [CAGradientLayer layer];
gr.frame = mySubview.frame;
gr.colors = [NSArray arrayWithObjects:
                     (id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:.5] CGColor]
                     ,(id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:.5] CGColor]
                     , nil];

gr.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0],[NSNumber numberWithFloat:1],nil];

[mySubview.layer insertSublayer:gr atIndex:0];

Hmm, estou tentando esse código exato e não está tendo nenhum efeito para mim. Minha subvisão é um UILabel adicionado como subview de cell.contentView e teste no iOS 6.0.1, caso isso importe.
Joe Strout

A que você aplica o código acima? E você tentou adicionar o rótulo simplesmente à exibição da célula?
Agat

Esta é a solução perfeita na minha opinião. O desenho da camada soluciona o problema perfeitamente, mantendo total flexibilidade. Não gosto da solução de usar o UIImageView, porque é mais difícil ajustar o gradiente ou a cor (você precisa criar uma nova imagem a cada vez) e subclassificar o UIView apenas por isso parece um exagero.
Erik van der Neut

@ Lyida, eu não sou realmente desenvolvedor Swift. (Eu ainda mais um c #). Mas, pelo que vi, isso não é algo específico de idioma, no entanto, principalmente a lógica do Cocoa / iOS Frameworks. Portanto, a idéia é apenas colocar CAGradientLayer quase transparente em sua visualização para obter o resultado solicitado.
Agat 28/05

9

Para o Swift 2.2, isso funciona

cell.selectionStyle = UITableViewCellSelectionStyle.None

e a razão é explicada por @ Andriy

É porque a célula de exibição de tabela altera automaticamente a cor de plano de fundo de todas as visualizações na exibição de conteúdo para o estado destacado.


8

Inspirado por Yatheesha BL 's resposta eu criei um UITableViewCell categoria / extensão que lhe permite ligar e desligar esse 'recurso' transparência.

Rápido

let cell = <Initialize Cell>
cell.keepSubviewBackground = true  // Turn  transparency "feature" off
cell.keepSubviewBackground = false // Leave transparency "feature" on

Objetivo-C

UITableViewCell* cell = <Initialize Cell>
cell.keepSubviewBackground = YES;  // Turn  transparency "feature" off
cell.keepSubviewBackground = NO;   // Leave transparency "feature" on

KeepBackgroundCell é compatível com CocoaPods. Você pode encontrá-lo no GitHub


7

Você pode cell.selectionStyle = UITableViewCellSelectionStyleNone;definir o backgroundColor como- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath


4

Inspirado pela resposta de Yatheesha BL .

Se você chamar super.setSelected (selecionado, animado: animado), ele limpará toda a cor de fundo que você definir . Portanto, não chamaremos super método.

Em Swift:

override func setSelected(selected: Bool, animated: Bool) {    
    if(selected)  {
        contentView.backgroundColor = UIColor.red 
    } else {
        contentView.backgroundColor = UIColor.white
    }
}

override func setHighlighted(highlighted: Bool, animated: Bool) {
    if(highlighted) {
        contentView.backgroundColor = UIColor.red 
    } else {
        contentView.backgroundColor = UIColor.white
    }
}

1
Obrigado pela solução. +1 Substituir a variável ishiglighted e o método sethighlighted são coisas diferentes. a resposta correta é substituir o método.
Numan Karaaslan

4

Para maiúsculas e minúsculas, estas são as tentativas para evitar a cor cinza de todos os itens da célula (caso você esteja usando a célula de exibição de tabela personalizada):

  1. Defina o SelectionStyle para .none 👉 selectionStyle = .none

  2. Substitua esse método.

    func setHighlighted (_ realçado: Bool, animado: Bool)

  3. Ligue para o super, para obter o benefício da super configuração.

    super.setHighlighted (realçado, animado: animado)

  4. Faça o que sempre destacando a lógica que você deseja.

    override func setHighlighted(_ highlighted: Bool, animated: Bool) {
          super.setHighlighted(highlighted, animated: animated)
          // Your Highlighting Logic goes here...
    }

3

UITableViewCell altera o backgroundColor de todas as subvisões na seleção por algum motivo.

Isso pode ajudar:

DVColorLockView

Use algo assim para impedir que o UITableView altere a cor da exibição durante a seleção.


1

Desenhe a vista em vez de definir a cor de fundo

import UIKit

class CustomView: UIView {

    var fillColor:UIColor!

    convenience init(fillColor:UIColor!) {
        self.init()
        self.fillColor = fillColor
    }

    override func drawRect(rect: CGRect) {
        if let fillColor = fillColor {
            let context = UIGraphicsGetCurrentContext()
            CGContextSetFillColorWithColor(context, fillColor.CGColor);
            CGContextFillRect (context, self.bounds);

        }
    }


}

1

Solução MAIS SIMPLES sem bugs com animação (como na resposta mais votada) e sem subclassificação e desenho - defina a cor da borda da camada em vez de backgroundColor e defina uma largura de borda muito grande.

colorThumb.layer.cornerRadius = 6
colorThumb.layer.borderWidth = colorThumb.frame.width
colorThumb.layer.borderColor = value.color

0

Tente o seguinte código:

-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{     
[super setHighlighted:highlighted animated:animated];
//Set your View's Color here.
}

0

Não se esqueça de substituir setSelected, bem comosetHighlighted

override func setHighlighted(highlighted: Bool, animated: Bool) {

    super.setHighlighted(highlighted, animated: animated)
    someView.backgroundColor = .myColour()
}

override func setSelected(selected: Bool, animated: Bool) {

    super.setSelected(selected, animated: animated)
    someView.backgroundColor = .myColour()
}

0

Isso é semelhante à resposta de Pavel Gurov, mas mais flexível na medida em que permite que qualquer cor seja permanente.

class PermanentBackgroundColorView: UIView {
    var permanentBackgroundColor: UIColor? {
        didSet {
            backgroundColor = permanentBackgroundColor
        }
    }

    override var backgroundColor: UIColor? {
        didSet {
            if backgroundColor != permanentBackgroundColor {
                backgroundColor = permanentBackgroundColor
            }
        }
    }
}

0

Eu queria manter o comportamento de seleção padrão, exceto uma subvisão de célula que desejava ignorar a alteração automática da cor de fundo. Mas eu também precisava mudar a cor do plano de fundo em outros momentos.

A solução que encontrei foi a subclasse, UIViewpara que ela ignore a configuração da cor de fundo normalmente e adicione uma função separada para ignorar a proteção.

Swift 4

class MyLockableColorView: UIView {
    func backgroundColorOverride(_ color: UIColor?) {
            super.backgroundColor = color
    }

    override var backgroundColor: UIColor? {
        set {
            return
        }
        get {
            return super.backgroundColor
        }
    }
}

-1

Aqui está a minha solução, use contentView para mostrar a seleçãoColor, ele funciona perfeitamente

#import "BaseCell.h"

@interface BaseCell ()
@property (nonatomic, strong) UIColor *color_normal;
@property (nonatomic, assign) BOOL needShowSelection;
@end


@implementation BaseCell
@synthesize color_customSelection;
@synthesize color_normal;
@synthesize needShowSelection;

- (void)awakeFromNib
{
    [super awakeFromNib];
    [self setup];
}

- (void)setup
{
    //save normal contentView.backgroundColor
    self.color_normal = self.backgroundColor;
    if (self.color_normal == nil) {
        self.color_normal = [UIColor colorWithRGBHex:0xfafafa];
    }
    self.color_customSelection = [UIColor colorWithRGBHex:0xF1F1F1];
    self.accessoryView.backgroundColor = [UIColor clearColor];
    if (self.selectionStyle == UITableViewCellSelectionStyleNone) {
        needShowSelection = NO;
    }
    else {
        //cancel the default selection
        needShowSelection = YES;
        self.selectionStyle = UITableViewCellSelectionStyleNone;
    }
}

/*
 solution is here
 */
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    if (needShowSelection) {
        self.contentView.backgroundColor = self.backgroundColor = color_customSelection;
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesCancelled:touches withEvent:event];
    if (needShowSelection) {
        self.contentView.backgroundColor = self.backgroundColor = color_normal;
    }
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];
    if (needShowSelection) {
        UIColor *color  = selected ? color_customSelection:color_normal;
        self.contentView.backgroundColor = self.backgroundColor = color;
    }
}

-1

Coloque esse código na sua subclasse de UITableViewCell

Sintaxe do Swift 3

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    if(selected) {
        lockerSmall.backgroundColor = UIColor.init(red: 233/255, green: 106/255, blue: 49/255, alpha: 1.0)
    }
}


override func setHighlighted(_ highlighted: Bool, animated: Bool) {
    super.setHighlighted(highlighted, animated: animated)

    if(highlighted) {
        lockerSmall.backgroundColor = UIColor.init(red: 233/255, green: 106/255, blue: 49/255, alpha: 1.0)
    }
}

-1

Adicionando outra solução se você estiver usando storyboards. Crie uma subclasse UIViewque não permita que ela backgroundColorseja configurada depois de configurada inicialmente.

@interface ConstBackgroundColorView : UIView

@end

@implementation ConstBackgroundColorView

- (void)setBackgroundColor:(UIColor *)backgroundColor {
    if (nil == self.backgroundColor) {
        [super setBackgroundColor:backgroundColor];
    }
}

@end

-1

Se a solução em segundo plano mencionada acima não estiver solucionando o problema, seu problema poderá estar presente no datasourceseu tableView.

Para mim, eu estava criando uma instância de um objeto DataSource (chamado BoxDataSource) para manipular os métodos delegate e dataSource tableView, da seguinte maneira:

//In cellForRowAtIndexPath, when setting up cell
let dataSource = BoxDataSource(delegate: self)
cell.tableView.dataSource = dataSource
return cell

Isso fazia com que o dataSource fosse desalocado sempre que a célula era tocada e, portanto, todo o conteúdo desaparecia. O motivo é que a ARC está desalocando / coletando a natureza.

Para corrigir isso, eu tive que ir para a célula personalizada, adicionar uma variável de fonte de dados:

//CustomCell.swift
var dataSource: BoxDataSource?

Em seguida, é necessário definir o dataSource para o dataSource da célula que varvocê acabou de criar no cellForRow, para que isso não seja desalocado com o ARC.

cell.statusDataSource = BoxAssigneeStatusDataSource(delegate: self)
cell.detailsTableView.dataSource = cell.statusDataSource
return cell

Espero que ajude.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.