La sottoview UITableViewCell scompare quando viene selezionata la cella


178

Sto implementando una vista della tabella di selezione dei colori in cui l'utente può selezionare, diciamo, 10 colori (dipende dal prodotto). L'utente può anche selezionare altre opzioni (come la capacità del disco rigido, ...).

Tutte le opzioni di colore sono all'interno della propria sezione di visualizzazione tabella.

Voglio visualizzare un quadratino a sinistra del textLabel che mostra il colore effettivo.

In questo momento sto aggiungendo un semplice UIView quadrato, dandogli il colore di sfondo corretto, in questo modo:

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

Questo visualizza bene senza problemi.

Il mio unico problema è che quando seleziono una riga "color", durante l'animazione della selezione da dissolvenza a blu, la mia UIView personalizzata (colorThumb) viene nascosta. Viene nuovamente visualizzato subito dopo la fine dell'animazione di selezione / deselezione, ma questo produce un brutto artefatto.

Cosa devo fare per correggere questo? Non inserisco la sottoview nel posto giusto?

(Non c'è niente di speciale in didSelectRowAtIndexPath, cambio semplicemente l'accessorio della cella in una casella o niente e deseleziono l'indicePath corrente).


Cos'è upadteCell?
Idan,

updateCell apporta alcune piccole modifiche come l'impostazione del segno di spunta o meno, la scelta del colore del testo in base alla disponibilità, ... ma nessuna modifica reale correlata alla cella stessa o al colorThumb.
Cyrille,

la risposta accettata non fornisce la soluzione, vedere la mia risposta di seguito per la soluzione
Pavel Gurov

Risposte:


227

UITableViewCellcambia il colore di sfondo di tutte le viste secondarie quando la cella è selezionata o evidenziata, puoi risolvere questo problema sovrascrivendo le celle di Tableview setSelected:animatedesetHighlighted:animated e resettando il colore di sfondo della vista.

Nell'obiettivo 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;
    }
}

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

Abbiamo bisogno delle condizioni if (highlighted)e if (selected)? Penso che funzionerà se non abbiamo queste condizioni.
Rishabh Tayal,

@RishabhTayal è fondamentalmente evitare di ignorare la variabile con lo stesso valore
cameloper

1
Tieni presente che quando cambi il colore di sfondo mentre l'elemento è selezionato, il vecchio colore (errato) potrebbe essere ripristinato quando l'elemento viene deselezionato. Se ciò può accadere, rimuovere le condizioni if ​​(evidenziate) e if (selezionate).
Marcel W,

Questo approccio annulla l'animazione, quindi non ha senso. Meglio impostare lo stile di selezione delle celle su .none.
Alexander Danilov,

122

È perché la cella della vista tabella cambia automaticamente il colore di sfondo di tutte le viste all'interno della vista contenuto per lo stato evidenziato. Puoi prendere in considerazione la sottoclasse UIViewper disegnare il tuo colore o utilizzarla UIImageViewcon un'immagine allungata 1x1 px personalizzata.


1
Stupidami Ovviamente, le visualizzazioni secondarie devono essere trasparenti in modo che l'animazione della selezione possa avvenire correttamente. Grazie!
Cyrille,

52
Oppure puoi reimpostare la setHighlighted:animated:setSelected:animated:
sostituzione del

1
In qualche modo il ripristino del colore di sfondo non ha funzionato per me (in esecuzione su iOS 8.1). Invece risolto questo subclassando la mia vista e sovrascrivendo setBackgroundColor come [super setBackgroundColor: [UIColor whiteColor]].
bizz84,

44

Ho trovato una soluzione piuttosto elegante invece di fare casini con i metodi di selezione / evidenziazione tableViewCell. È possibile creare una sottoclasse di UIView che ignora l'impostazione del colore di sfondo per cancellare il colore.

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

Versione Obj-C:

@interface NeverClearView : UIView

@end

@implementation NeverClearView

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

@end

È adorabile. Facilmente la soluzione migliore se si dispone di una vista riutilizzabile come un "badge" o "tag" che non dovrebbe mai avere uno sfondo chiaro. :: rant :: che soluzione di confondimento @UIKit, impostando tutte le viste figlio su trasparenti quando si effettua una selezione di celle. Almeno limite alle viste figlio che sono l'altezza o la larghezza della cella o quelle a N profondità.
Semplicemente

@SimplGy La profondità dell'evidenziazione sarebbe davvero un'opzione dolce, ma hey - questa è UIKit, ho visto cose molto peggio di così =)
Pavel Gurov

3
Ha funzionato per me dopo aver modificato if in CGColorGetAlpha (backgroundColor! .CGColor) == 0 poiché non era uguale a clearColor
piltdownman7

9

Un altro modo per gestire il problema è riempire la vista con un gradiente di grafica principale, come:

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

Hmm, sto provando questo codice esatto e non ha alcun effetto per me. La mia subview è una UILabel aggiunta come subview di cell.contentView e test in iOS 6.0.1, nel caso in cui ciò sia importante.
Joe Strout,

A cosa si applica il codice sopra? E hai provato ad aggiungere l'etichetta semplicemente alla vista della cella?
Agat,

Questa è la soluzione perfetta secondo me. Disegnare sul livello risolve perfettamente il problema, pur mantenendo la massima flessibilità. Non mi piace la soluzione dell'uso di UIImageView, perché è più difficile regolare la sfumatura o il colore (dovresti creare una nuova immagine ogni volta) e sottoclassare UIView solo per questo sembra eccessivo.
Erik van der Neut,

@Lyida, in realtà non sono sviluppatore Swift. (Ho anche più C # -one). Ma da quello che ho visto, non è una cosa piuttosto specifica della lingua, tuttavia, principalmente la logica di Cocoa / iOS Frameworks. Quindi, l'idea è solo di posizionare CAGradientLayer quasi trasparente nella tua vista per ottenere il risultato richiesto.
Agat,

9

Per Swift 2.2 funziona

cell.selectionStyle = UITableViewCellSelectionStyle.None

e la ragione è spiegata da @ Andriy

È perché la cella della vista tabella cambia automaticamente il colore di sfondo di tutte le viste all'interno della vista contenuto per lo stato evidenziato.


8

Ispirato da Yatheesha BL 's risposta ho creato un UITableViewCell categoria / estensione che permette di attivare e disattivare questa trasparenza 'caratteristica'.

veloce

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

KeepBackgroundCell è compatibile con CocoaPods. Puoi trovarlo su GitHub


7

È possibile cell.selectionStyle = UITableViewCellSelectionStyleNone;, quindi impostare backgroundColor su- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath


4

Ispirato da Yatheesha BL .

Se chiami super.setSelezionato (selezionato, animato: animato), cancellerà tutto il colore di sfondo impostato . Quindi, non chiameremo metodo super.

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

1
Grazie per la soluzione +1 Sostituire la variabile ishiglighted e il metodo sethighlighted sono cose diverse. la risposta corretta è ignorare il metodo.
Numan Karaaslan,

4

Ad esempio, sono i sett per evitare di ottenere il colore grigio per tutti gli elementi nella cella (nel caso in cui si stia utilizzando una cella di visualizzazione tabella personalizzata):

  1. Impostare lo stile di selezione su .none 👉 selectionStyle = .none

  2. Sostituisci questo metodo.

    func setHighlighted (_ evidenziato: Bool, animato: Bool)

  3. Chiama il super, per ottenere il vantaggio del super setup.

    super.setHighlighted (evidenziato, animato: animato)

  4. Fai ciò che mai evidenziando la logica che desideri.

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

3

UITableViewCell cambia il colore di sfondo di tutte le visualizzazioni secondarie sulla selezione per qualche motivo.

Questo potrebbe aiutare:

DVColorLockView

Utilizzare qualcosa del genere per impedire a UITableView di modificare il colore della vista durante la selezione.


1

Disegna la vista invece di impostare il colore di sfondo

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

        }
    }


}

1

Soluzione PIÙ SEMPLICE senza bug con animazione (come nella risposta più votata) e senza sottoclasse e disegno: imposta il colore del bordo del livello anziché lo sfondo Colore e imposta una larghezza del bordo molto grande.

colorThumb.layer.cornerRadius = 6
colorThumb.layer.borderWidth = colorThumb.frame.width
colorThumb.layer.borderColor = value.color

0

Prova il seguente codice:

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

0

Non dimenticare di annullare setSelectedanchesetHighlighted

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

0

Questo è simile alla risposta di Pavel Gurov, ma più flessibile in quanto consente a qualsiasi colore di essere permanente.

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

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

0

Volevo mantenere il comportamento di selezione predefinito ad eccezione di una sottoview di cella che volevo ignorare il cambio automatico del colore di sfondo. Ma dovevo anche essere in grado di cambiare il colore di sfondo altre volte.

La soluzione che mi è venuta in mente è stata la sottoclasse in UIViewmodo da ignorare l'impostazione del colore di sfondo normalmente e aggiungere una funzione separata per bypassare la protezione.

Swift 4

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

    override var backgroundColor: UIColor? {
        set {
            return
        }
        get {
            return super.backgroundColor
        }
    }
}

-1

ecco la mia soluzione, usa contentView per mostrare selectionColor, funziona perfettamente

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

-1

Inserire questo codice nella sottoclasse di UITableViewCell

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

-1

Aggiunta di un'altra soluzione se si utilizzano storyboard. Creare una sottoclasse UIViewche non consenta backgroundColordi essere impostato dopo che è stato inizialmente impostato.

@interface ConstBackgroundColorView : UIView

@end

@implementation ConstBackgroundColorView

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

@end

-1

Se la soluzione in background sopra menzionata non risolve il problema, il problema potrebbe risiedere nel datasourcetuo tableView.

Per me, stavo creando un'istanza di un oggetto DataSource (chiamato BoxDataSource) per gestire i metodi tableView del delegato e dataSource, in questo modo:

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

Ciò causava la deallocazione di DataSource ogni volta che si toccava la cella e quindi tutto il contenuto scompariva. Il motivo è che ARC sta deallocando / immondizia raccogliendo natura.

Per risolvere questo problema, ho dovuto andare nella cella personalizzata, aggiungere una variabile dell'origine dati:

//CustomCell.swift
var dataSource: BoxDataSource?

Quindi, è necessario impostare dataSource sull'origine dati della cella varappena creata in cellForRow, quindi questo non viene allocato con ARC.

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

Spero che aiuti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.