web-dev-qa-db-fra.com

UIView backgroundColor disparaît lorsque UITableViewCell est sélectionné

J'ai une construction simple tableViewCell dans le constructeur d'interface . Elle contient un UIView qui contient une image . Maintenant, lorsque je sélectionne la cellule, l'arrière-plan de sélection bleu par défaut est affiché, mais la couleur d'arrière-plan de mon UIView a disparu. 

Le fichier d'implémentation de mon UITableViewCell ne fait rien de spécial. Il suffit d'init & retourne soi-même et tout ce que je fais dans setSelected est super appel.

Comment puis-je obtenir mon backgroundColor UIView à afficher lorsque la tableView est sélectionnée?

66
P5ycH0

Le problème ici est que la [super] mise en œuvre de

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

définit toutes les couleurs d'arrière-plan de UITableViewCell sur rgba (0,0,0,0). Pourquoi? Peut-être pour nous faire tous transpirer? 

Ce n'est pas que des vues entières disparaissent (comme le montre le fait que si vous modifiez les propriétés de la bordure du calque des vues, celles-ci sont conservées).

Voici la séquence des appels de fonction résultant du contact avec une cellule

  1. setHighlighted 
  2. touche 
  3. layoutSubviews 
  4. willSelectRowAtIndexPath (côté délégué) 
  5. setSelected (!!! c'est ici que toutes les couleurs de fond de votre vue doivent disparaître) 
  6. didSelectRowAtIndexPath (côté délégué) 
  7. setSelected (again) (Il est intéressant de noter que les couleurs d'arrière-plan ne sont pas effacées lors de cet appel. Quelle étrangeté se passe à l'intérieur de cette super méthode?)
  8. layoutSubviews (encore)

Donc, vos options sont de

  1. Remplacer - (void) setSelected: (BOOL) sélectionné animé: (BOOL) animé; sans appeler [super setSelected: sélectionné animé: animé] . Cela vous donnera l'implémentation la plus techniquement correcte car a) le code est encapsulé dans la sous-classe UITableViewCell et b) car il est appelé uniquement lorsque cela est nécessaire (deux fois si nécessaire, mais il existe peut-être un moyen de contourner ce problème). L'inconvénient est que vous devrez ré-implémenter toutes les fonctions nécessaires (par opposition aux fonctions de nettoyage de couleur inutiles) de setSelected. Maintenant, ne me demandez pas comment remplacer correctement setSelected pour l'instant. Votre proposition est aussi bonne que la mienne pour le moment (soyez patient, je modifierai cette réponse une fois que je l'aurai trouvée).
  2. Réaffirmez les couleurs d'arrière-plan dans didSelectRowAtIndexPath . Ce n'est pas terrible car cela place ce qui devrait être du code d'instance en dehors de l'instance. Il a l'avantage de n'être appelé que lorsque c'est nécessaire, par opposition à ...
  3. Réaffirmez les couleurs d'arrière-plan dans layoutSubviews . Ce n'est pas génial du tout parce que layoutSubviews s'appelle comme MILLION de fois! On l'appelle chaque fois que la table est rafraîchie, chaque fois qu'elle défile, chaque fois que votre grand-mère reçoit une permanente ... comme un sérieux, un million de fois. Cela signifie qu'il y a beaucoup de réaffirmations d'arrière-plan inutiles et beaucoup de temps système supplémentaire de traitement. Du côté positif, il place le code dans la sous-classe UITableViewCell, qui est Nice.

Malheureusement, réaffirmer les couleurs d'arrière-plan dans setHighlighted ne fait rien car setHighlighted est appelé avant que toutes les couleurs d'arrière-plan ne soient définies sur [r: 0 b: 0 g: 0 a: 0] lors du premier appel à setSelected.

// TODO: Donnez une excellente description de la façon de remplacer setSelected (restez à l'écoute)

105
Brooks
- (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;
}
68
mientus

Auparavant, j'ai fait comme @ P5ycH0 a dit (image 1x1 étirée), mais après @Brooks, je me suis dit que remplacer -(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated dans mon implémentation UITableViewCell personnalisée et réinitialiser les couleurs d'arrière-plan après avoir appelé [super setHighlighted:highlighted animated:animated]; garde mes couleurs d'arrière-plan lorsque la cellule est sélectionnée

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

Lorsque votre UITableViewCell est sélectionné, vous devez prêter attention à deux états: Highlighted et Selected

Ainsi, pour les scénarios où vous avez une classe de cellule personnalisée qui est la sous-classe de UITableViewCell, vous pouvez facilement remplacer ces deux méthodes pour éviter cette situation (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
    }

}
15
kubilay

Brooks a une bonne explication à cela, mais je pense avoir une meilleure solution.

Dans votre sous-vue, remplacez setBackgroundColor: par la couleur de votre choix. Le passeur sera toujours appelé, mais seule la couleur spécifiée sera appliquée.

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

Vous devez remplacer les deux méthodes suivantes dans votre cellule personnalisée:

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

Notez que:

  • vous devez appeler [super setSelected:animated:] et [super setHighlighted:animated:] au début de votre implémentation personnalisée ou de vos méthodes correspondantes;
  • vous devez définir le UITableViewCellSelectionStyleNone selectionStyle pour votre cellule personnalisée, afin de désactiver tout style UITableViewCell par défaut;

Voici l'exemple de la mise en oeuvre:

- (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 est la propriété de UITableViewCell qui apparaît dans iOS 7. Notez que vous pouvez utiliser la ou les vues enfant de votre propre cellule à la place.

3
Igor Vasilev

Ce problème peut (enfin) être résolu dans iOS 13. Trouvez ce joli paragraphe dans les notes de publication d'iOS 13 beta 3.

La classe UITableViewCell ne modifie plus les propriétés backgroundColor ou isOpaque de contentView ni aucune de ses sous-vues lorsque les cellules sont mises en surbrillance ou sélectionnées. Si vous définissez une couleur d'arrière-plan opaque sur une sous-vue de la cellule à l'intérieur (et y compris) de contentView, l'apparence à laquelle la cellule est mise en surbrillance ou sélectionnée peut être affectée. Le moyen le plus simple de résoudre tout problème avec vos sous-vues est de s'assurer que leur propriété backgroundColor est définie sur nil ou sur clear et que leur propriété opaque est définie sur false. Toutefois, si nécessaire, vous pouvez remplacer les méthodes setHighlighted (: animated :) et setSelected (: animated :) pour modifier manuellement ces propriétés dans vos sous-vues lors du déplacement. vers ou depuis les états en surbrillance et sélectionnés. (13955336)

https://developer.Apple.com/documentation/ios_ipados_release_notes/ios_ipados_13_beta_3_release_notes

3
PatrickDotStar

Ok, perdre la couleur d’arrière-plan d’une classe UIView est un comportement normal quand il se trouve dans une tableviewcell sélectionnée. pixel blanc 1x1 étiré . Moche imo, mais cela fonctionne.

3
P5ycH0

En rapport avec la réponse de @ Brooks, voici ce que j'ai fait pour que cela fonctionne dans Swift et iOS8/iOS9.

  • Remplacer les setSelected et setHighlighted
  • N'appelle pas super
  • Effacez le contentView.backgroundColor, car il ne doit pas nécessairement s’étendre sur toute la largeur de la cellule (accessoires).
  • Utilisez le backgroundColor de la cellule elle-même et réglez-le en conséquence.

    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()
        }
    }
    
1
Kevin R

Ajoutez ceci à votre 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()
        })
    }
}
1
Tanel Teemusk

Je viens de passer un peu de temps sur cette question étrange. Je ne voulais pas définir le style UITableViewCellSelectionStyleNone pour conserver l'animation Nice lors de la sélection de ma ligne. Mais aucune des idées suggérées ne fonctionnait pour moi - j'essayais de remplacer setSelected et setHighlighted et de placer ma sous-vue backgroundColor ici - la réinitialisation continuelle par iOS et le clignotement (nouvelle couleur -> ancienne couleur) . Pour moi, le correctif était assez simple . Lorsque ma ligne est sélectionnée, un autre contrôleur de vue est poussé, l'utilisateur choisit une option sur cet écran et le délégué est appelé lorsque je change de couleur en fonction de la sélection de l'utilisateur. Dans ce délégué, je viens de faire [cellule setSelected: NO animated: NO] pour ma cellule. (J'ai statique UITableViewController et des sorties vers les cellules) . Vous pouvez probablement désélectionner une cellule dans la méthode didSelect, mais dans mon cas, j'utilise des légendes.

0
dimaclopin

Voici mon point de vue là-dessus. J'ai une sous-classe dont toute ma cellule hérite, c'est donc comme cela que je le fais pour éviter le changement d'arrière-plan dans mes 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]
        }
    }
}

Cela corrige automatiquement le problème pour tous les UIImageView.

0
allaire

Dans iOS 7, ce qui a fonctionné pour moi est de remplacer setSelected:animated: dans la sous-classe UITableViewCell, mais contrairement à la suggestion de @Brooks, j'ai appelé [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];
}

Cela me permet de conserver l'animation de sélection par défaut du système lorsque l'utilisateur appuie sur la cellule et de la désélectionner dans la colonne didSelectRowAtIndexPath: du délégué de la vue de table.

0
Matthew Quiros

Vous pouvez modifier le comportement de tableViewCell en remplaçant la fonction setHighlighted dans la classe UITableViewCell (vous devrez en hériter). Exemple de code dans lequel je change l'image de fond de ma cellule:

// 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"]; 
    }
}

Vous pouvez également modifier le mode de sélection en gris, bleu ou aucun dans le générateur d'interface.

0
CedricSoubrie

A partir de là, vous avez dit que vous avez construit une tableViewCell en utilisant IB. J'aimerais vérifier si vous ajoutez votre vue en tant que sous-vue de contentView de UITableViewCell, et non de view. La vue du contenu est la vue par défaut du contenu affiché par la cellule.

De la référence:

La vue du contenu d'un objet UITableViewCell est la vue d'ensemble par défaut du contenu affiché par la cellule. Si vous souhaitez personnaliser des cellules en ajoutant simplement des vues supplémentaires, vous devez les ajouter à la vue du contenu afin qu'elles soient correctement positionnées lorsque la cellule passe en mode édition et en sort.

0
MHC

Swift 4

Dans votre classe UITableViewCell:

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

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