ios uitableview uiview interface-builder

ios - UIView backgroundColor desaparece cuando se selecciona UITableViewCell



interface-builder (16)

Acabo de pasar un tiempo en este extraño problema. No quería configurar el estilo UITableViewCellSelectionStyleNone para conservar una buena animación cuando se seleccionó mi fila. Pero ninguna de las ideas sugeridas funcionó para mí: estaba intentando anular setSelected y establecer Highlighted y establecer mi subview backgroundColor allí; seguía reiniciando por iOS y sigue parpadeando (color nuevo -> color antiguo). Para mí, la solución fue bastante simple. Cuando se selecciona mi fila, se presiona otro controlador de vista, el usuario elige alguna opción en esa pantalla y se llama a un delegado donde cambio el color en función de la selección del usuario. En este delegado acabo de hacer [celda setSelected: NO animated: NO] para mi celda. (Tengo UITableViewController estático y tengo salidas a las células). Probablemente puedas anular la selección de la celda en el método didSelect, pero en mi caso estoy usando segues.

Tengo una compilación tableViewCell simple en el constructor de interfaz. Contiene una UIView que contiene una imagen. Ahora, cuando selecciono la celda, se muestra el fondo de selección azul predeterminado, pero el backgroundcolor de mi UIView se ha ido.

El archivo de implementación de mi UITableViewCell no hace nada especial. Simplemente inicia y regresa a sí mismo y todo lo que hago en setSelected es call super.

¿Cómo puedo mostrar mi UIView backgroundColor cuando se selecciona tableView?


Agregue esto a su UITableViewCell

override func setHighlighted(highlighted: Bool, animated: Bool) { super.setHighlighted(false, animated: animated) if highlighted { self.backgroundColor = UIColor.blueColor() }else{ UIView.animateWithDuration(0.2, animations: { self.backgroundColor = UIColor.clearColor() }) } }


Anteriormente lo he hecho como @ P5ycH0 dijo (imagen 1x1 estirada), pero después de @Brooks pensé que anulación -(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated en mi implementación personalizada UITableViewCell y restablecer los colores de fondo después llamando [super setHighlighted:highlighted animated:animated]; mantiene mis colores de fondo cuando se selecciona / resalta la celda

-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { [super setHighlighted:highlighted animated:animated]; myView.backgroundColor = myColor; }


Aquí está mi opinión sobre esto. Tengo una subclase de la que toda mi celda hereda, así que así es como lo hago para evitar el cambio de fondo en mis UIImageViews:

override func setHighlighted(highlighted: Bool, animated: Bool) { var backgroundColors = [UIView: UIColor]() for view in contentView.subviews as [UIView] { if let imageView = view as? UIImageView { backgroundColors[imageView] = imageView.backgroundColor } } super.setHighlighted(highlighted, animated: animated) for view in contentView.subviews as [UIView] { if let imageView = view as? UIImageView { imageView.backgroundColor = backgroundColors[imageView] } } } override func setSelected(selected: Bool, animated: Bool) { var backgroundColors = [UIView: UIColor]() for view in contentView.subviews as [UIView] { if let imageView = view as? UIImageView { backgroundColors[imageView] = imageView.backgroundColor } } super.setSelected(selected, animated: animated) for view in contentView.subviews as [UIView] { if let imageView = view as? UIImageView { imageView.backgroundColor = backgroundColors[imageView] } } }

Esto soluciona automáticamente el problema para todo UIImageView .


Brooks tiene una gran explicación de por qué sucede esto, pero creo que tengo una mejor solución.

En su subvista, anule setBackgroundColor: a cualquier color que desee. Todavía se llamará al colocador, pero solo se aplicará el color especificado.

- (void)setBackgroundColor:(UIColor *)backgroundColor { [super setBackgroundColor:[UIColor whiteColor]]; }


Cuando se selecciona UITableViewCell , hay dos estados a los que debe prestar atención: Highlighted y Selected .

Por lo tanto, para los escenarios en los que tiene una clase de celda personalizada que es la subclase de UITableViewCell , puede anular fácilmente estos dos métodos para evitar esta situación (Swift):

class MyCell: UITableViewCell { @IBOutlet var myView: UIView! override func setHighlighted(highlighted: Bool, animated: Bool) { let myViewBackgroundColor = myView.backgroundColor super.setHighlighted(highlighted, animated: animated) myView.backgroundColor = myViewBackgroundColor } override func setSelected(selected: Bool, animated: Bool) { let myViewBackgroundColor = myView.backgroundColor super.setSelected(selected, animated: animated) myView.backgroundColor = myViewBackgroundColor } }


De acuerdo, perder el color de fondo de una clase UIView es un comportamiento normal cuando está en una tabla vista seleccionada. No pude encontrar la forma de prevenir eso. Ahora acabo de reemplazar el UIView con un UIImageView que contiene un pixel blanco 1x1 estirado. Feo, pero funciona.


Debe anular los siguientes dos métodos en su celda personalizada:

- (void) setSelected:(BOOL)selected animated:(BOOL)animated; - (void) setHighlighted:(BOOL)highlighted animated:(BOOL)animated;

Tenga en cuenta que:

  • debe llamar a [super setSelected:animated:] y [super setHighlighted:animated:] al comienzo de su implementación personalizada o los métodos correspondientes;
  • debe establecer el UITableViewCellSelectionStyleNone selección UITableViewCellSelectionStyleNone para su celda personalizada, para deshabilitar cualquier estilo UITableViewCell predeterminado;

Aquí el ejemplo de la implementación:

- (void) setHighlighted:(BOOL)highlighted animated:(BOOL)animated { [super setHighlighted:highlighted animated:animated]; [self setHighlightedSelected:highlighted animated:animated]; } - (void) setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; [self setHighlightedSelected:selected animated:animated]; } - (void) setHighlightedSelected:(BOOL)selected animated:(BOOL)animated { void(^selection_block)(void) = ^ { self.contentView.backgroundColor = selected ? SELECTED_BACKGROUND_COLOR : NORMAL_BACKGROUND_COLOR; }; if(animated) { [UIView animateWithDuration:SELECTION_ANIMATION_DURATION delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:selection_block completion:NULL]; } else selection_block(); }

contentView es propiedad de UITableViewCell que apareció en iOS 7. Tenga en cuenta que puede usar la vista secundaria o vistas de su propia celda en lugar de ella.


Después de decir que construiste un tableViewCell usando IB, me gustaría comprobar si estás agregando tu vista como una subvista de contentView de UITableViewCell, no de view . La vista de contenido es la supervista predeterminada para el contenido que muestra la celda.

De la referencia:

La vista de contenido de un objeto UITableViewCell es la supervista predeterminada para el contenido que muestra la celda. Si desea personalizar las celdas simplemente agregando vistas adicionales, debe agregarlas a la vista de contenido para que se ubiquen apropiadamente a medida que la celda ingresa y sale del modo de edición.


El problema aquí es que la [súper] implementación de

- (void) setSelected:(BOOL) selected animated:(BOOL) animated;

establece todos los colores de fondo en UITableViewCell a rgba (0,0,0,0). ¿Por qué? ¿Quizás para hacernos sudar a todos?

No es que desaparezcan vistas completas (como lo demuestra el hecho de que si cambia las propiedades del borde de la capa de vistas, se conservan)

Aquí está la secuencia de llamadas de función que resulta de tocar una celda

  1. setHighlighted
  2. toquesEnded
  3. layoutSubviews
  4. willSelectRowAtIndexPath (lado delegado)
  5. setSelected (! aquí es donde todos los colores de fondo de su vista deben desaparecer)
  6. didSelectRowAtIndexPath (lado delegado)
  7. setSelected (otra vez) (Curiosamente, los colores de fondo no se borraron en esta llamada. ¿Qué extrañeza está sucediendo dentro de ese súper método?)
  8. layoutSubviews (otra vez)

Entonces tus opciones son

  1. Override - (void) setSelected: (BOOL) seleccionado animado: (BOOL) animado; sin llamar [super setSeleccionado: seleccionado animado: animado] . Esto le dará la implementación más correcta técnicamente porque a) el código está envuelto dentro de la subclase UITableViewCell yb) porque solo se llama cuando es necesario (bien dos veces cuando es necesario, pero tal vez hay una forma de evitarlo). El inconveniente es que tendrá que volver a implementar todas las funciones necesarias (a diferencia de las funciones innecesarias de borrado de color) de setSelected. Ahora no me pregunte cómo anular adecuadamente setSelected por el momento. Tu suposición es tan buena como la mía por ahora (ten paciencia, voy a editar esta respuesta una vez que la encuentre).
  2. Vuelva a afirmar los colores de fondo en didSelectRowAtIndexPath . Esto no es tan bueno porque coloca lo que debería ser código de instancia fuera de la instancia. Tiene la ventaja de que solo se llama cuando es necesario, en lugar de ...
  3. Reafirma los colores de fondo en layoutSubviews . ¡Esto no es genial en absoluto porque layoutSubviews se llama como A MILLION veces! Se llama cada vez que la mesa se actualiza, cada vez que se desplaza, cada vez que su abuela obtiene una permanente ... como en serio, un millón de veces. Eso significa que hay muchas reafirmaciones de fondo innecesarias y una gran cantidad adicional de procesamiento adicional. En el lado positivo, coloca el código dentro de la subclase UITableViewCell, lo cual es bueno.

Desafortunadamente, volver a afirmar los colores de fondo en setHighlighted no hace nada porque se llama a setHighlighted antes de que todos los colores de fondo se configuren en [r: 0 b: 0 g: 0 a: 0] en la primera llamada a setSelected.

// TODO: proporciona una excelente descripción de cómo anular setSelected (estad atentos)


En iOS 7, lo que funcionó para mí es anular setSelected:animated: en la subclase UITableViewCell , pero al contrario de la sugerencia de @Brooks, llamé a [super setSelected:selected animated:animated] .

- (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Reassert the background color of the color view so that it shows // even when the cell is highlighted for selection. self.colorView.backgroundColor = [UIColor blueColor]; }

Esto me permite mantener la animación de selección predeterminada del sistema cuando el usuario toca la celda, y también para anular la selección en la didSelectRowAtIndexPath: de delegado de la vista de tabla didSelectRowAtIndexPath:


En relación con la respuesta de @ Brooks, esto es lo que hice para que funcione en Swift y iOS8 / iOS9.

  • Reemplazar el conjunto setSelected y establecer setHighlighted
  • No llames a super
  • Elimine el contentView.backgroundColor , ya que no tiene que abarcar todo el ancho de la celda (es decir, accesorios).
  • Use el backgroundColor de la celda y configúrelo como corresponda.

    class AwesomeTableViewCell: UITableViewCell { private struct Constants { static var highlightedColor = UIColor.greenColor() static var selectedColor = UIColor.redColor() static let animationTime = NSTimeInterval(0.2) } override func awakeFromNib() { super.awakeFromNib() contentView.backgroundColor = UIColor.clearColor() backgroundColor = AppContext.sharedInstance.theme.colors.background } override func setHighlighted(highlighted: Bool, animated: Bool) { if animated { UIView.animateWithDuration(Constants.animationTime, animations: { () -> Void in self.setHighlighted(highlighted) }) } else { self.setHighlighted(highlighted) } } override func setSelected(selected: Bool, animated: Bool) { if animated { UIView.animateWithDuration(Constants.animationTime, animations: { () -> Void in self.setSelected(selected) }) } else { self.setSelected(selected) } } private func setHighlighted(highlighted: Bool) { backgroundColor = highlighted ? Constants.highlightedColor : UIColor.whiteColor() } private func setSelected(selected: Bool) { backgroundColor = selected ? Constants.selectedColor : UIColor.whiteColor() } }


Puede cambiar el comportamiento de tableViewCell anulando la función establecidaHighlighted en la clase UITableViewCell (tendrá que heredar de ella). Ejemplo de mi código donde cambio la imagen de fondo de mi celular:

// animate between regular and highlighted state - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated; { [super setHighlighted:highlighted animated:animated]; //Set the correct background Image UIImageView* backgroundPicture = (UIImageView*)[self viewWithTag:HACK_BACKGROUND_VIEW_TAG]; if (highlighted) { backgroundPicture.image = [UIImage imageNamed:@"FondSelected.png"]; } else { backgroundPicture.image = [UIImage imageNamed:@"Fond.png"]; } }

También puede cambiar el modo de selección a gris, azul o ninguno en el constructor de interfaz.


Resumen

Esta solución permite bloquear algunos de los colores de fondo de una celda , mientras que el resto se controla mediante el comportamiento del sistema.

Basado en la answer mientus, he creado una solución que le permite especificar qué vistas deben mantener su color de fondo .

Esto aún permite que otras subvistas de celda tengan su fondo eliminado al resaltar / seleccionar, y es la única solución que funciona en nuestro caso (dos vistas que necesitan un fondo permanente).

Usé un enfoque orientado al protocolo, con un protocolo BackgroundLockable que contiene la lista de vistas para bloquear, y ejecutando un cierre manteniendo los colores:

protocol BackgroundLockable { var lockedBackgroundViews: [UIView] { get } func performActionWithLockedViews(_ action: @escaping () -> Void) } extension BackgroundLockable { func performActionWithLockedViews(_ action: @escaping () -> Void) { let lockedViewToColorMap = lockedBackgroundViews.reduce([:]) { (partialResult, view) -> [UIView: UIColor?] in var mutableResult = partialResult mutableResult[view] = view.backgroundColor return mutableResult } action() lockedViewToColorMap.forEach { (view: UIView, color: UIColor?) in view.backgroundColor = color } } }

Luego tengo una subclase de UITableViewCell , que anula el resaltado y la selección para ejecutar el cierre del protocolo al invocar el comportamiento predeterminado (super):

class LockableBackgroundTableViewCell: UITableViewCell, BackgroundLockable { var lockedBackgroundViews: [UIView] { return [] } override func setHighlighted(_ highlighted: Bool, animated: Bool) { performActionWithLockedViews { super.setHighlighted(highlighted, animated: animated) } } override func setSelected(_ selected: Bool, animated: Bool) { performActionWithLockedViews { super.setSelected(selected, animated: animated) } } }

Ahora solo tengo que LockableBackgroundTableViewCell subclase de LockableBackgroundTableViewCell o utilizar el protocolo BackgroundLockable en una clase de celda para agregar fácilmente el comportamiento de bloqueo a algunas celdas.

class SomeCell: LockableBackgroundTableViewCell { @IBOutlet weak var label: UILabel! @IBOutlet weak var icon: UIImageView! @IBOutlet weak var button: UIButton! override var lockedBackgroundViews: [UIView] { return [label, icon] } }


Swift 4

En tu clase UITableViewCell :

override func setSelected(_ selected: Bool, animated: Bool) { myView.backgroundColor = UIColor.blue } override func setHighlighted(_ highlighted: Bool, animated: Bool) { myView.backgroundColor = UIColor.blue }


- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { UIColor *backgroundColor = self.channelImageView.backgroundColor; [super setHighlighted:highlighted animated:animated]; self.channelImageView.backgroundColor = backgroundColor; } - (void)setSelected:(BOOL)selected animated:(BOOL)animated { UIColor *backgroundColor = self.channelImageView.backgroundColor; [super setSelected:selected animated:animated]; self.channelImageView.backgroundColor = backgroundColor; }