La sous-vue UITableViewCell disparaît lorsque la cellule est sélectionnée

178

J'implémente une vue de table de sélection de couleurs où l'utilisateur peut sélectionner parmi, par exemple, 10 couleurs (dépend du produit). L'utilisateur peut également sélectionner d'autres options (comme la capacité du disque dur, ...).

Toutes les options de couleur se trouvent dans leur propre section d'affichage de table.

Je veux afficher un petit carré à gauche du textLabel montrant la couleur réelle.

En ce moment, j'ajoute un simple UIView carré, donnez-lui la bonne couleur d'arrière-plan, comme ceci:

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

Cela affiche bien sans problèmes.

Mon seul problème est que lorsque je sélectionne une ligne "couleur", lors de l'animation de sélection de fondu au bleu, mon UIView personnalisé (colorThumb) est masqué. Il redevient visible juste après la fin de l'animation de sélection / désélection, mais cela produit un artefact laid.

Que dois-je faire pour corriger cela? Est-ce que je n'insère pas la sous-vue au bon endroit?

(Il n'y a rien de spécial dans le didSelectRowAtIndexPath, je change simplement l'accessoire de la cellule en une case à cocher ou rien et désélectionne l'indexPath actuel).

Cyrille
la source
Qu'est-ce que upadteCell?
Idan
updateCell fait quelques ajustements mineurs comme définir la coche ou non, choisir la couleur du texte en fonction de la disponibilité, ... mais aucun changement réel lié à la cellule elle-même ou au colorThumb.
Cyrille
la réponse acceptée ne fournit pas la solution, voir ma réponse ci-dessous pour la solution
Pavel Gurov

Réponses:

227

UITableViewCellchange la couleur de fond de tous les sous vues lorsque la cellule est sélectionnée ou en surbrillance, vous pouvez résoudre ce problème en remplaçant la cellule de Tableview setSelected:animatedet setHighlighted:animatedet la réinitialisation de la couleur de fond de la vue.

Dans l'objectif 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;
    }
}

Dans 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
la source
Avons-nous besoin des conditions if (highlighted)et if (selected)? Je pense que cela fonctionnera si nous n'avons pas ces conditions.
Rishabh Tayal
@RishabhTayal, c'est essentiellement pour éviter de remplacer la variable avec la même valeur
cameloper
1
Notez que lorsque vous modifiez la couleur d'arrière-plan alors que l'élément est sélectionné, l'ancienne (mauvaise) couleur peut être restaurée lorsque l'élément n'est plus sélectionné. Si cela peut arriver, supprimez les conditions if (en surbrillance) et if (sélectionnées).
Marcel W
Cette approche annule l'animation, elle n'a donc aucun sens. Mieux vaut définir le style de sélection de cellule sur .aucun.
Alexander Danilov
122

C'est parce que la cellule de vue de tableau change automatiquement la couleur d'arrière-plan de toutes les vues dans la vue de contenu pour l'état en surbrillance. Vous pouvez envisager de sous-classer UIViewpour dessiner votre couleur ou utiliserUIImageView avec une image étirée personnalisée 1x1 px.

Andriy
la source
1
Dumb moi. Bien entendu, les sous-vues doivent être transparentes pour que l'animation de sélection puisse se produire correctement. Merci!
Cyrille
52
Ou vous pouvez réinitialiser le setHighlighted:animated:setSelected:animated:
remplacement de
1
La réinitialisation de la couleur d'arrière-plan n'a pas fonctionné pour moi (sous iOS 8.1). Au lieu de cela résolu cela en sous-classant ma vue et en remplaçant setBackgroundColor comme [super setBackgroundColor: [UIColor whiteColor]].
bizz84
44

J'ai trouvé une solution assez élégante au lieu de jouer avec les méthodes de sélection / mise en évidence de tableViewCell. Vous pouvez créer une sous-classe d'UIView qui ignore la définition de sa couleur d'arrière-plan sur une couleur claire.

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

Version Obj-C:

@interface NeverClearView : UIView

@end

@implementation NeverClearView

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

@end
Pavel Gurov
la source
C'est adorable. Facilement la meilleure solution si vous avez une vue réutilisable comme un "badge" ou un "tag" qui ne devrait jamais avoir un arrière-plan clair. :: coup de gueule :: quelle solution confondante @UIKit, mettant toutes les vues enfants sur transparentes lorsque vous faites une sélection de cellule. Limitez au moins les vues enfants qui correspondent à la hauteur ou à la largeur totale de la cellule, ou celles à N profondeur.
SimplGy
@SimplGy La profondeur de la mise en évidence serait en effet une option douce, mais bon - c'est UIKit, j'ai vu des choses tellement pires que ça =)
Pavel Gurov
3
A travaillé pour moi après avoir changé le if en CGColorGetAlpha (backgroundColor! .CGColor) == 0 car il n'était pas égal à clearColor
piltdownman7
9

Une autre façon de gérer le problème consiste à remplir la vue avec un dégradé graphique de base, comme:

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
la source
Hmm, j'essaye ce code exact et cela n'a aucun effet pour moi. Ma sous-vue est une UILabel ajoutée en tant que sous-vue de cell.contentView et testée sous iOS 6.0.1, au cas où cela importerait.
Joe Strout
À quoi appliquez-vous le code ci-dessus? Et avez-vous essayé d'ajouter l'étiquette simplement à la vue de la cellule?
Agat
C'est la solution parfaite à mon avis. Le dessin sur la couche résout parfaitement le problème, tout en conservant une flexibilité totale. Je n'aime pas la solution d'utiliser une UIImageView, car il est alors plus difficile d'ajuster le dégradé ou la couleur (vous devrez créer une nouvelle image à chaque fois), et sous-classer UIView juste pour cela semble exagéré.
Erik van der Neut
@Lyida, je ne suis pas vraiment développeur Swift. (J'ai encore plus C # -one). Mais d'après ce que j'ai vu, ce n'est pas plutôt une chose spécifique à la langue, cependant, principalement la logique Cocoa / iOS Frameworks. Donc, l'idée est juste de placer CAGradientLayer presque transparent dans votre vue pour obtenir le résultat demandé.
Agat
9

Pour Swift 2.2, cela fonctionne

cell.selectionStyle = UITableViewCellSelectionStyle.None

et la raison est expliquée par @ Andriy

C'est parce que la cellule de vue de tableau change automatiquement la couleur d'arrière-plan de toutes les vues dans la vue de contenu pour l'état en surbrillance.

swiftBoy
la source
8

Inspiré par Yatheesha BL de réponse J'ai créé une catégorie / extension de UITableViewCell qui vous permet d'activer et de désactiver cette transparence « fonctionnalité ».

Rapide

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

Objectif c

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

KeepBackgroundCell est compatible avec les CocoaPods. Vous pouvez le trouver sur GitHub

Tim Bodeit
la source
7

Vous pouvez cell.selectionStyle = UITableViewCellSelectionStyleNone;, puis définir le backgroundColor à- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath

gumpwang
la source
4

Inspiré de la réponse de Yatheesha BL .

Si vous appelez super.setSelected (sélectionné, animé: animé), il effacera toutes les couleurs d'arrière-plan que vous avez définies . Donc, nous n'appellerons pas super méthode.

Dans 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
la source
1
Merci pour la solution. +1 Remplacer la variable ishiglighted et la méthode sethighlighted sont des choses différentes. la bonne réponse est de remplacer la méthode.
Numan Karaaslan
4

Dans certains cas, voici les Septs pour éviter d'obtenir la couleur grise pour tous les éléments de la cellule (au cas où vous utilisez une cellule de vue de tableau personnalisée):

  1. Définissez selectionStyle sur .none 👉 selectionStyle = .none

  2. Remplacez cette méthode.

    func setHighlighted (_ surligné: Bool, animé: Bool)

  3. Appelez le super, pour profiter de la super configuration.

    super.setHighlighted (surligné, animé: animé)

  4. Faites ce que vous voulez pour mettre en évidence la logique.

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

UITableViewCell modifie le backgroundColor de toutes les sous-vues lors de la sélection pour une raison quelconque.

Cela pourrait aider:

DVColorLockView

Utilisez quelque chose comme ça pour empêcher UITableView de changer la couleur de votre vue pendant la sélection.

DylanVann
la source
1

Dessinez la vue au lieu de définir la couleur d'arrière-plan

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
la source
1

Solution LA PLUS SIMPLE sans bugs avec animation (comme dans la réponse la mieux notée) et sans sous-classification et dessin - définissez la couleur de la bordure du calque au lieu de backgroundColor et définissez une très grande largeur de bordure.

colorThumb.layer.cornerRadius = 6
colorThumb.layer.borderWidth = colorThumb.frame.width
colorThumb.layer.borderColor = value.color
Alexandre Danilov
la source
0

Essayez le code suivant:

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

N'oubliez pas de remplacer setSelectedainsi quesetHighlighted

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
la source
0

Ceci est similaire à la réponse de Pavel Gurov, mais plus flexible en ce sens qu'elle permet à n'importe quelle couleur d'être permanente.

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

    override var backgroundColor: UIColor? {
        didSet {
            if backgroundColor != permanentBackgroundColor {
                backgroundColor = permanentBackgroundColor
            }
        }
    }
}
user3352495
la source
0

Je voulais conserver le comportement de sélection par défaut, sauf pour une sous-vue de cellule que je voulais ignorer le changement de couleur d'arrière-plan automatique. Mais j'avais aussi besoin de pouvoir changer la couleur d'arrière-plan à d'autres moments.

La solution que j'ai trouvée était de sous-classe UIViewafin qu'elle ignore le réglage de la couleur d'arrière-plan normalement et ajoute une fonction distincte pour contourner la protection.

Swift 4

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

    override var backgroundColor: UIColor? {
        set {
            return
        }
        get {
            return super.backgroundColor
        }
    }
}
Zekel
la source
-1

voici ma solution, utilisez contentView pour afficher selectionColor, ça marche parfaitement

#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;
    }
}
pseudo
la source
-1

Placez ce code dans votre sous-classe de UITableViewCell

Syntaxe 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
la source
-1

Ajout d'une autre solution si vous utilisez des storyboards. Créez une sous-classe de UIViewqui ne permet pas backgroundColorde définir le après sa définition initiale.

@interface ConstBackgroundColorView : UIView

@end

@implementation ConstBackgroundColorView

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

@end
mofojed
la source
-1

Si la solution d'arrière-plan mentionnée ci-dessus ne datasourcerésout pas votre problème, votre problème peut résider dans votre for your tableView.

Pour moi, je BoxDataSourcecréais une instance d'un objet DataSource (appelé ) pour gérer les méthodes délégué et dataSource tableView, comme suit:

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

Cela provoquait la désallocation de la source de données chaque fois que la cellule était exploitée, et donc tout le contenu disparaissait. La raison en est la nature de la désallocation / récupération de la mémoire ARC.

Pour résoudre ce problème, j'ai dû aller dans la cellule personnalisée, ajouter une variable de source de données:

//CustomCell.swift
var dataSource: BoxDataSource?

Ensuite, vous devez définir la source de données sur la source de données de la cellule que varvous venez de créer dans cellForRow, afin qu'elle ne soit pas désallouée avec ARC.

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

J'espère que cela pourra aider.

Josh O'Connor
la source