La subvista UITableViewCell desaparece cuando se selecciona la celda

178

Estoy implementando una vista de tabla de selección de color donde el usuario puede seleccionar, por ejemplo, 10 colores (depende del producto). El usuario también puede seleccionar otras opciones (como la capacidad del disco duro, ...).

Todas las opciones de color están dentro de su propia sección de vista de tabla.

Quiero mostrar un pequeño cuadrado a la izquierda de la etiqueta de texto que muestra el color real.

En este momento estoy agregando un simple UIView cuadrado, darle el color de fondo correcto, como este:

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

Esto se muestra bien sin problemas.

Mi único problema es que cuando selecciono una fila de "color", durante la animación de selección de fundido a azul, se oculta mi UIView (colorThumb) personalizada. Se vuelve a ver justo después de que finalizó la animación de selección / deselección, pero esto produce un feo artefacto.

¿Qué debo hacer para corregir esto? ¿No inserto la subvista en el lugar correcto?

(No hay nada especial en didSelectRowAtIndexPath, solo cambio el accesorio de la celda a una casilla de verificación o nada, y deselecciono el indexPath actual).

Cirilo
fuente
¿De qué se trata upadteCell?
Idan
updateCell realiza algunos ajustes menores, como establecer la marca de verificación o no, elegir el color del texto según la disponibilidad, ... pero no hay ningún cambio real relacionado con la celda en sí o el colorThumb.
Cyrille
la respuesta aceptada no proporciona la solución, vea mi respuesta a continuación para la solución
Pavel Gurov

Respuestas:

227

UITableViewCellcambia el color de fondo de todos los puntos de vista sub cuando se selecciona o resalta celular, puede resolver este problema primordial de la célula TableView setSelected:animatedy setHighlighted:animatedy restablecer el color de fondo Vista.

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

En 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
    }
}
Yatheesha BL
fuente
¿Necesitamos las condiciones if (highlighted)y if (selected)? Creo que funcionará si no tenemos esas condiciones.
Rishabh Tayal
@RishabhTayal es básicamente para evitar anular la variable con el mismo valor
cameloper
1
Tenga en cuenta que cuando cambia el color de fondo mientras el elemento está seleccionado, el color antiguo (incorrecto) puede restaurarse cuando el elemento no se selecciona. Si eso puede suceder, elimine las condiciones if (resaltadas) y if (seleccionadas).
Marcel W
Este enfoque cancela la animación, por lo que no tiene sentido. Es mejor establecer el estilo de selección de celda en .none.
Alexander Danilov
122

Esto se debe a que la celda de vista de tabla cambia automáticamente el color de fondo de todas las vistas dentro de la vista de contenido para el estado resaltado. Puede considerar subclasificar UIViewpara dibujar su color o usar UIImageViewcon una imagen estirada personalizada de 1x1 px.

Andriy
fuente
1
Tonto yo Por supuesto, eso fue todo, las subvistas deben ser transparentes para que la animación de selección pueda ocurrir correctamente. ¡Gracias!
Cyrille
52
O puede volver a configurar el color de fondo setHighlighted:animated:ysetSelected:animated:
pt2ph8
1
De alguna manera, restablecer el color de fondo no funcionó para mí (ejecutándose en iOS 8.1). En cambio, resolví esto subclasificando mi vista y anulando setBackgroundColor como [super setBackgroundColor: [UIColor whiteColor]].
bizz84
44

Encontró una solución bastante elegante en lugar de jugar con los métodos de selección / resaltado tableViewCell. Puede crear una subclase de UIView que ignore establecer su color de fondo para borrar el color.

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

Versión Obj-C:

@interface NeverClearView : UIView

@end

@implementation NeverClearView

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

@end
Pavel Gurov
fuente
Esto es adorable. Fácilmente la mejor solución si tiene una vista reutilizable como una "insignia" o "etiqueta" que nunca debería tener un fondo claro. :: rant :: qué solución confusa @UIKit, establecer todas las vistas secundarias en transparentes cuando haces una selección de celda Al menos, limite las vistas secundarias que tienen la altura o el ancho completo de la celda, o las que tienen N profundidad.
SimplGy
@SimplGy La profundidad del resaltado sería una buena opción, pero bueno, esto es UIKit, he visto cosas mucho peores que esto =)
Pavel Gurov
3
Me funcionó después de cambiar el if a CGColorGetAlpha (backgroundColor! .CGColor) == 0 ya que no era igual a clearColor
piltdownman7
9

Otra forma de manejar el problema es llenar la vista con un degradado de gráficos centrales, 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];
Agat
fuente
Hmm, estoy probando este código exacto y no tiene ningún efecto para mí. Mi subvista es una UILabel agregada como una subvista de cell.contentView y pruebas bajo iOS 6.0.1, en caso de que sea importante.
Joe Strout
¿A qué aplica el código anterior? ¿Y ha tratado de agregar la etiqueta simplemente a la vista de celda?
Agat
Esta es la solución perfecta en mi opinión. Dibujar en la capa aborda el problema perfectamente, manteniendo la flexibilidad total. No me gusta la solución de usar un UIImageView, porque entonces es más difícil ajustar el degradado o el color (tendrías que hacer una nueva imagen cada vez), y subclasificar UIView solo por esto parece excesivo.
Erik van der Neut
@Lyida, en realidad no soy Swift-developer. (Incluso más C # -una). Pero por lo que he visto, eso no es algo específico del idioma, sin embargo, principalmente uno lógico de Cocoa / iOS Frameworks. Por lo tanto, la idea es colocar CAGradientLayer casi transparente en su vista para obtener el resultado solicitado.
Agat
9

Para Swift 2.2 esto funciona

cell.selectionStyle = UITableViewCellSelectionStyle.None

y la razón es explicada por @ Andriy

Esto se debe a que la celda de vista de tabla cambia automáticamente el color de fondo de todas las vistas dentro de la vista de contenido para el estado resaltado.

swiftBoy
fuente
8

Inspirado por Yatheesha BL 's respuesta creé una categoría UITableViewCell / extensión que le permite encender y apagar esta 'característica' transparencia.

Rápido

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

C objetivo

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

KeepBackgroundCell es compatible con CocoaPods. Puedes encontrarlo en GitHub

Tim Bodeit
fuente
7

Puede cell.selectionStyle = UITableViewCellSelectionStyleNone;, luego establecer el color de fondo en- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath

gumpwang
fuente
4

Inspirado por Yatheesha BL .

Si llama a super.setSelected (seleccionado, animado: animado), borrará todo el color de fondo que establezca . Por lo tanto, no llamaremos super método.

En 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
    }
}
Milan Kamilya
fuente
1
Gracias por la solucion. +1 Reemplazar la variable ishiglighted y el método sethighlighted son cosas diferentes. La respuesta correcta es anular el método.
Numan Karaaslan
4

Para el caso de mayo, este es el Septs para evitar obtener el color gris para todos los elementos en la celda (en caso de que esté usando una celda de vista de tabla personalizada):

  1. Establezca selectionStyle en .none 👉 selectionStyle = .none

  2. Anular este método.

    func setHighlighted (_ resaltado: Bool, animado: Bool)

  3. Llame al super, para obtener el beneficio de la super configuración.

    super.setHighlighted (resaltado, animado: animado)

  4. Haz lo que quieras resaltando la lógica que quieras.

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

UITableViewCell cambia el color de fondo de todas las subvistas en la selección por alguna razón.

Esto podría ayudar:

DVColorLockView

Use algo así para evitar que UITableView cambie el color de su vista durante la selección.

DylanVann
fuente
1

Dibuje la vista en lugar de establecer el color de fondo

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

        }
    }


}
PeiweiChen
fuente
1

Solución SIMPLEST sin errores con animación (como en la respuesta mejor calificada) y sin subclases y dibujos: configure el color del borde de la capa en lugar de backgroundColor y establezca un ancho de borde muy grande.

colorThumb.layer.cornerRadius = 6
colorThumb.layer.borderWidth = colorThumb.frame.width
colorThumb.layer.borderColor = value.color
Alexander Danilov
fuente
0

Pruebe el siguiente código:

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

No olvide anular setSelectedtan bien 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()
}
Magoo
fuente
0

Esto es similar a la respuesta de Pavel Gurov, pero más flexible ya que permite que cualquier color sea permanente.

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

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

Quería mantener el comportamiento de selección predeterminado, excepto para una subvista de celda que quería ignorar el cambio automático de color de fondo. Pero también necesitaba poder cambiar el color de fondo en otros momentos.

La solución que se me ocurrió fue crear una subclase, UIViewpor lo que ignora la configuración del color de fondo normalmente y agrega una función separada para evitar la protección.

Swift 4

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

    override var backgroundColor: UIColor? {
        set {
            return
        }
        get {
            return super.backgroundColor
        }
    }
}
zekel
fuente
-1

aquí está mi solución, use contentView para mostrar selectionColor, funciona perfectamente

#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;
    }
}
Mella
fuente
-1

Coloque este código en su subclase de UITableViewCell

Sintaxis de 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)
    }
}
Jay Mayu
fuente
-1

Agregar otra solución si está usando guiones gráficos. Crear una subclase de UIVieweso no permite backgroundColorque se establezca después de que se establece inicialmente.

@interface ConstBackgroundColorView : UIView

@end

@implementation ConstBackgroundColorView

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

@end
mofojed
fuente
-1

Si la solución de fondo mencionada anteriormente no está solucionando su problema, su problema puede estar en su datasourcepara su TableView.

Para mí, estaba creando una instancia de un objeto DataSource (llamado BoxDataSource) para manejar los métodos delegado y dataSource tableView, así:

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

Esto causaba que la fuente de datos se desasignara cada vez que se tocaba la celda, y por lo tanto, todo el contenido desaparecía. La razón es que la asignación de ARC / recolección de basura es natural.

Para solucionar esto, tuve que ir a la celda personalizada, agregar una variable de fuente de datos:

//CustomCell.swift
var dataSource: BoxDataSource?

Luego, debe establecer el origen de datos en el origen de datos de la celda varque acaba de crear en cellForRow, por lo que esto no se desasigna con ARC.

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

Espero que ayude.

Josh O'Connor
fuente