Le comportement UITableViewCell de mon projet actuel me laisse perplexe. J'ai une sous-classe assez simple de UITableViewCell. Il ajoute quelques éléments supplémentaires à la vue de base (via [self.contentView addSubview:...]
et définit les couleurs d'arrière-plan sur les éléments pour leur donner l'aspect de boîtes rectangulaires noires et grises.
Etant donné que l’arrière-plan de la table entière a cette image de texture concrète, l’arrière-plan de chaque cellule doit être transparent, même lorsqu’il est sélectionné, mais dans ce cas, il doit s’assombrir légèrement. J'ai défini un arrière-plan sélectionné semi-transparent personnalisé pour obtenir cet effet:
UIView *background = [[[UIView alloc] initWithFrame:self.bounds] autorelease];
background.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
background.opaque = NO;
[self setSelectedBackgroundView:background];
Et bien que cela donne le bon rendu pour l'arrière-plan, un effet secondaire étrange se produit lorsque je sélectionne la cellule; tous les autres fonds sont en quelque sorte désactivés désactivés . Voici une capture d'écran. La cellule du bas ressemble à ce qu'elle devrait être et n'est pas sélectionnée. La cellule du haut est sélectionnée, mais elle devrait afficher les zones rectangulaires noires et grises, mais elles sont parties!
Qui sait ce qui se passe ici et plus important encore: comment puis-je corriger cela?
En réalité, chaque sous-vue de TableViewCell recevra les méthodes setSelected et setHighlighted. La méthode setSelected supprimera les couleurs d'arrière-plan, mais si vous la définissez pour l'état sélectionné, elle sera corrigée.
Par exemple, si ce sont des étiquettes UIL ajoutées en tant que sous-vues dans votre cellule personnalisée, vous pouvez ajouter cela à la méthode setSelected de votre code d'implémentation TableViewCell:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
self.textLabel.backgroundColor = [UIColor blackColor];
}
où self.textLabel serait quoi que soient ces étiquettes qui sont montrées dans l'image ci-dessus
Je ne sais pas où vous ajoutez votre vue sélectionnée, je l'ajoute généralement dans la méthode setSelected.
Vous pouvez également sous-classer UILabel et substituer la méthode setHighlighted de la manière suivante:
-(void)setHighlighted:(BOOL)highlighted
{
[self setBackgroundColor:[UIColor blackColor]];
}
Le processus de mise en évidence des cellules peut sembler complexe et déroutant si vous ne savez pas ce qui se passe. J'étais complètement confus et ai fait quelques expériences approfondies. Voici les notes sur mes découvertes qui pourraient aider quelqu'un (si quelqu'un a quelque chose à ajouter à cela ou à réfuter, merci de le commenter et j'essaierai de le confirmer et de le mettre à jour)
Dans l'état normal «non sélectionné»
selectedBackgroundView
est cachéebackgroundView
est visible (ainsi, si votre contentView est transparent, vous voyez la backgroundView
ou (si vous n'avez pas défini une backgroundView
, vous verrez la couleur d'arrière-plan de la UITableView
elle-même)Une cellule est sélectionnée, les événements suivants se produisent immédiatement sans aucune animation:
backgroundColor
de toutes les vues/sous-vues dans contentView est effacée (ou définie sur transparente), la couleur de texte de l'étiquette, etc. est remplacée par la couleur sélectionnéeselectedBackgroundView
devient visible (cette vue est toujours la taille de la cellule (un cadre personnalisé est ignoré, utilisez une sous-vue si nécessaire). Notez également que la backgroundColor
de subViews
ne s'affiche pas pour une raison quelconque, peut-être est-elle transparente comme le contentView
). Si vous n'avez pas défini selectedBackgroundView
, Cocoa créera/insérera le fond dégradé bleu (ou gris) et l'affichera pour vous.)backgroundView
est inchangéeLorsque la cellule est désélectionnée, une animation pour supprimer la mise en surbrillance commence:
selectedBackgroundView
alpha est animée de 1.0 (totalement opaque) à 0.0 (totalement transparent).backgroundView
est à nouveau inchangée (l’animation ressemble donc à un fondu enchaîné entre selectedBackgroundView
et backgroundView
)contentView
sera redessinée dans l’état "non sélectionné" et sa sous-vue backgroundColor
redeviendra visible (cela peut rendre votre animation horrible, il est donc conseillé de ne pas utiliser UIView.backgroundColor
dans votre contentView
).CONCLUSIONS:
Si vous avez besoin que backgroundColor
persiste dans l'animation de surbrillance, n'utilisez pas la propriété backgroundColor
de UIView
, vous pouvez plutôt essayer (probablement avec tableview:cellForRowAtIndexPath:
):
Un CALayer avec une couleur de fond:
UIColor *bgColor = [UIColor greenColor];
CALayer* layer = [CALayer layer];
layer.frame = viewThatRequiresBGColor.bounds;
layer.backgroundColor = bgColor.CGColor;
[cell.viewThatRequiresBGColor.layer addSublayer:layer];
ou une couche CAGradientLayer:
UIColor *startColor = [UIColor redColor];
UIColor *endColor = [UIColor purpleColor];
CAGradientLayer* gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = viewThatRequiresBGColor.bounds;
gradientLayer.colors = @[(id)startColor.CGColor, (id)endColor.CGColor];
gradientLayer.locations = @[[NSNumber numberWithFloat:0],[NSNumber numberWithFloat:1]];
[cell.viewThatRequiresBGColor.layer addSublayer:gradientLayer];
J'ai également utilisé une technique CALayer.border pour fournir un séparateur UITableView personnalisé:
// We have to use the borderColor/Width as opposed to just setting the
// backgroundColor else the view becomes transparent and disappears during
// the cell's selected/highlighted animation
UIView *separatorView = [[UIView alloc] initWithFrame:CGRectMake(0, 43, 1024, 1)];
separatorView.layer.borderColor = [UIColor redColor].CGColor;
separatorView.layer.borderWidth = 1.0;
[cell.contentView addSubview:separatorView];
Lorsque vous commencez à faire glisser un UITableViewCell, il appelle setBackgroundColor:
dans ses sous-vues avec une couleur 0 alpha. J'ai résolu ce problème en sous-classant UIView et en redéfinissant setBackgroundColor:
pour ignorer les demandes avec des couleurs 0 alpha. Il semble hacky, mais il est plus propre que n’importe lequel des autressolutions j’ai rencontré.
@implementation NonDisappearingView
-(void)setBackgroundColor:(UIColor *)backgroundColor {
CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
if (alpha != 0) {
[super setBackgroundColor:backgroundColor];
}
}
@end
Ensuite, j'ajoute une NonDisappearingView
à ma cellule et y ajoute d'autres sous-vues:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
UIView *background = [cell viewWithTag:backgroundTag];
if (background == nil) {
background = [[NonDisappearingView alloc] initWithFrame:backgroundFrame];
background.tag = backgroundTag;
background.backgroundColor = backgroundColor;
[cell addSubview:background];
}
// add other views as subviews of background
...
}
return cell;
}
Vous pouvez également faire de cell.contentView une instance de NonDisappearingView
.
Ma solution consiste à enregistrer la backgroundColor
et à la restaurer après le super appel.
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
UIColor *bgColor = self.textLabel.backgroundColor;
[super setSelected:selected animated:animated];
self.textLabel.backgroundColor = bgColor;
}
Vous devez également faire la même chose avec -setHighlighted:animated:
.
J'ai créé une catégorie/extension UITableViewCell qui vous permet d'activer et de désactiver cette "fonctionnalité" de transparence.
Installez-le via CocoaPods en ajoutant la ligne suivante à votre fichier podfile:
pod 'KeepBackgroundCell'
Swift
let cell = <Initialize Cell>
cell.keepSubviewBackground = true // Turn transparency "feature" off
cell.keepSubviewBackground = false // Leave transparency "feature" on
Objective-C
UITableViewCell* cell = <Initialize Cell>
cell.keepSubviewBackground = YES; // Turn transparency "feature" off
cell.keepSubviewBackground = NO; // Leave transparency "feature" on
Nous avons trouvé une solution assez élégante au lieu de jouer avec les méthodes tableView. Vous pouvez créer une sous-classe d'UIView qui ignore la définition de la couleur d'arrière-plan pour la couleur claire. Code:
class NeverClearView: UIView {
override var backgroundColor: UIColor? {
didSet {
if UIColor.clearColor().isEqual(backgroundColor) {
backgroundColor = oldValue
}
}
}
}
La version Obj-C serait similaire, l'essentiel ici est l'idée
Après avoir lu toutes les réponses existantes, nous avons trouvé une solution élégante utilisant Swift en ne classant que UITableViewCell.
extension UIView {
func iterateSubViews(block: ((view: UIView) -> Void)) {
for subview in self.subviews {
block(view: subview)
subview.iterateSubViews(block)
}
}
}
class CustomTableViewCell: UITableViewCell {
var keepSubViewsBackgroundColorOnSelection = false
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
// MARK: Overrides
override func setSelected(selected: Bool, animated: Bool) {
if self.keepSubViewsBackgroundColorOnSelection {
var bgColors = [UIView: UIColor]()
self.contentView.iterateSubViews() { (view) in
guard let bgColor = view.backgroundColor else {
return
}
bgColors[view] = bgColor
}
super.setSelected(selected, animated: animated)
for (view, backgroundColor) in bgColors {
view.backgroundColor = backgroundColor
}
} else {
super.setSelected(selected, animated: animated)
}
}
override func setHighlighted(highlighted: Bool, animated: Bool) {
if self.keepSubViewsBackgroundColorOnSelection {
var bgColors = [UIView: UIColor]()
self.contentView.iterateSubViews() { (view) in
guard let bgColor = view.backgroundColor else {
return
}
bgColors[view] = bgColor
}
super.setHighlighted(highlighted, animated: animated)
for (view, backgroundColor) in bgColors {
view.backgroundColor = backgroundColor
}
} else {
super.setHighlighted(highlighted, animated: animated)
}
}
}
Tout ce dont nous avons besoin est de remplacer la méthode setSelected et de modifier selectedBackgroundView pour la tableViewCell dans la classe personnalisée tableViewCell.
Nous devons ajouter la vue de fond pour la table tableViewCell dans la méthode cellForRowAtIndexPath.
lCell.selectedBackgroundView = [[UIView alloc] init];
Ensuite, j'ai remplacé la méthode setSelected comme indiqué ci-dessous.
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
UIImageView *lBalloonView = [self viewWithTag:102];
[lBalloonView setBackgroundColor:[[UIColor hs_globalTint] colorWithAlphaComponent:0.2]];
UITextView *lMessageTextView = [self viewWithTag:103];
lMessageTextView.backgroundColor = [UIColor clearColor];
UILabel *lTimeLabel = [self viewWithTag:104];
lTimeLabel.backgroundColor = [UIColor clearColor];
}
L'un des points les plus importants à noter est également de changer le style de sélection de tableViewCell. Ce ne devrait pas être UITableViewCellSelectionStyleNone.
lTableViewCell.selectionStyle = UITableViewCellSelectionStyleGray;