webentwicklung-frage-antwort-db.com.de

UIView backgroundColor verschwindet, wenn UITableViewCell ausgewählt wird

Ich habe einen einfachen tableViewCell-Build-Interface-Builder ..__, der eine UIView enthält, die ein Bild enthält ..__ Wenn ich nun die Zelle auswähle, wird der Standardhintergrund für die blaue Auswahl angezeigt, aber die Hintergrundfarbe meiner UIView ist weg. 

Die UITableViewCell-Implementierungsdatei hat keine besonderen Auswirkungen. Es ist nur init's und kehrt selbst zurück und alles, was ich in setSelected mache, ist call super.

Wie kann ich meine UIView backgroundColor anzeigen lassen, wenn die tableView ausgewählt ist?

66
P5ycH0

Das Problem hier ist, dass die Implementierung von

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

setzt alle Hintergrundfarben in der UITableViewCell auf rgba (0,0,0,0). Warum? Vielleicht um uns alle ins Schwitzen zu bringen? 

Es ist nicht so, dass ganze Ansichten verschwinden (was durch die Tatsache belegt wird, dass die Eigenschaften der Ansichtenebenen der Ansichtsebene geändert werden.)

Hier ist die Reihenfolge der Funktionsaufrufe, die sich aus dem Berühren einer Zelle ergibt

  1. setHighlighted 
  2. berührtEnded 
  3. layoutSubviews 
  4. willSelectRowAtIndexPath (Delegatenseite) 
  5. setSelected (!!! hier werden alle Hintergrundfarben Ihrer Ansicht verschwinden) 
  6. didSelectRowAtIndexPath (Delegatenseite) 
  7. setSelected (wieder) (Interessanterweise werden die Hintergrundfarben bei diesem Aufruf nicht gelöscht. Welche Fremdheit passiert in dieser Supermethode?)
  8. layoutSubviews (wieder)

Also haben Sie die Wahl zu

  1. Überschreiben - (void) setSelected: (BOOL) ausgewähltes animiertes: (BOOL) animiertes; ohne Aufruf [super setAusgewählt: ausgewählt animiert: animiert] . Dadurch erhalten Sie die technisch korrekteste Implementierung, weil a) der Code in der UITableViewCell-Unterklasse eingeschlossen ist und b) er nur aufgerufen wird, wenn er benötigt wird (bei Bedarf zweimal, aber vielleicht gibt es eine Möglichkeit, dies zu umgehen). Der Nachteil ist, dass Sie alle erforderlichen Funktionen (im Gegensatz zu unnötigen Farblöschfunktionen) von setSelected neu implementieren müssen. Nun frag mich nicht, wie ich setSelected jetzt richtig überschreiben kann. Ihre Vermutung ist jetzt so gut wie meine (seien Sie geduldig, ich bearbeite diese Antwort, sobald ich es herausgefunden habe).
  2. Setzen Sie die Hintergrundfarben in didSelectRowAtIndexPath erneut fest. Dies ist nicht so großartig, da der Instanzcode außerhalb der Instanz platziert wird. Es hat den Vorteil, dass es nur aufgerufen wird, wenn es benötigt wird, im Gegensatz zu ...
  3. Setzen Sie die Hintergrundfarben in layoutSubviews erneut fest. Das ist überhaupt nicht so toll, weil layoutSubviews wie eine Million Mal bezeichnet wird! Sie wird jedes Mal aufgerufen, wenn die Tabelle aktualisiert wird, jedes Mal, wenn sie scrollen, jedes Mal, wenn Ihre Großmutter eine Dauerwelle erhält ... wie ernsthaft, eine Million Mal. Dies bedeutet, dass viele Hintergrundüberprüfungen im Hintergrund unnötig sind und viel zusätzlicher Verarbeitungsaufwand anfällt. Auf der positiven Seite wird der Code in die UITableViewCell-Unterklasse eingefügt, die Nizza ist.

Leider macht das erneute Durchsetzen der Hintergrundfarben in setHighlighted nichts, da setHighlighted aufgerufen wird, bevor alle Hintergrundfarben beim ersten Aufruf von setSelected auf [r: 0 b: 0 g: 0 a: 0] gesetzt werden.

// TODO: Gib eine großartige Beschreibung, wie setSelected überschrieben werden soll.

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

Früher habe ich wie @ P5ycH0 gesagt (1x1 Bild gestreckt), aber nach @Brooks habe ich mir gedacht, dass das Überschreiben von -(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated in meiner benutzerdefinierten UITableViewCell-Implementierung und das Zurücksetzen der Hintergrundfarben nach dem Aufruf von [super setHighlighted:highlighted animated:animated]; meine Hintergrundfarben beibehält, wenn die Zelle ausgewählt/hervorgehoben wird

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

Wenn Ihre UITableViewCell ausgewählt ist, gibt es zwei Zustände, auf die Sie achten sollten: Highlighted und Selected

In Szenarien, in denen Sie über eine benutzerdefinierte Zellklasse verfügen, die Unterklasse von UITableViewCell ist, können Sie diese beiden Methoden leicht überschreiben, um diese Situation zu vermeiden (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 hat eine großartige Erklärung dafür, warum dies passiert, aber ich denke, ich habe eine bessere Lösung.

Überschreiben Sie in Ihrer Unteransicht setBackgroundColor: mit der gewünschten Farbe. Der Setter wird weiterhin aufgerufen, aber nur Ihre angegebene Farbe wird erzwungen.

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

Sie müssen die nächsten beiden Methoden in Ihrer benutzerdefinierten Zelle überschreiben:

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

Beachten Sie, dass:

  • sie sollten [super setSelected:animated:] und [super setHighlighted:animated:] zu Beginn Ihrer benutzerdefinierten Implementierung oder entsprechenden Methoden aufrufen.
  • sie sollten den UITableViewCellSelectionStyleNone selectionStyle für Ihre benutzerdefinierte Zelle festlegen, um das standardmäßige UITableViewCell-Styling zu deaktivieren.

Hier das Beispiel der Implementierung:

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

Die Variable contentView ist die Eigenschaft von UITableViewCell, die in iOS 7 angezeigt wird. Beachten Sie, dass Sie stattdessen die untergeordnete Ansicht oder die eigene Ansicht Ihrer eigenen Zelle verwenden können.

3
Igor Vasilev

Dieses Problem kann (endlich) in iOS 13 behoben werden. Fand diesen süßen Absatz in den Versionshinweisen zu iOS 13 Beta 3.

Die UITableViewCell-Klasse ändert nicht mehr die backgroundColor- oder isOpaque-Eigenschaften der contentView und ihrer Unteransichten, wenn Zellen hervorgehoben oder ausgewählt werden. Wenn Sie eine undurchsichtige Hintergrundfarbe für Unteransichten der Zelle innerhalb (und einschließlich) der Inhaltsansicht festlegen, wirkt sich dies möglicherweise auf das Erscheinungsbild aus, wenn die Zelle hervorgehoben oder ausgewählt wird. Die einfachste Möglichkeit, Probleme mit Ihren Unteransichten zu beheben, besteht darin, sicherzustellen, dass die backgroundColor-Eigenschaft auf null oder klar und die undurchsichtige Eigenschaft auf falsch gesetzt ist. Bei Bedarf können Sie jedoch die Methoden setHighlighted (: animated :) und setSelected (: animated :) überschreiben, um diese Eigenschaften in Ihren Unteransichten beim Verschieben manuell zu ändern zu oder von den markierten und ausgewählten Zuständen. (13955336)

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

3
PatrickDotStar

Ok, die Hintergrundfarbe einer UIView-Klasse zu verlieren ist ein normales Verhalten, wenn sie in einer ausgewählten tableviewcell ..__ ist 1x1 weißes Pixel gestreckt . Hässlich imo, aber es funktioniert.

3
P5ycH0

In Verbindung mit der Antwort von @ Brooks habe ich dies in Swift und iOS8/iOS9 funktionieren lassen.

  • Überschreiben Sie setSelected und setHighlighted.
  • Ruf nicht super an
  • Löschen Sie den contentView.backgroundColor, da er sich nicht über die gesamte Breite der Zelle (d. H. Zubehör) erstrecken muss.
  • Verwenden Sie backgroundColor der Zelle selbst und stellen Sie es entsprechend ein.

    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

Fügen Sie dies Ihrer UITableViewCell hinzu

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

Zusammenfassung

Diese Lösung lässt Sie sperren Sie einige Hintergrundfarben einer Zelle, während der Rest durch das Systemverhalten gesteuert wird.


Basierend auf mientus ' answer habe ich eine Lösung erstellt, mit der Sie angeben, welche Ansichten ihre Hintergrundfarbe beibehalten sollen

Auf diese Weise können andere Unteransichten der Zelle beim Hervorheben/Auswählen entfernt werden. Dies ist die einzige Lösung, die in unserem Fall funktioniert (zwei Ansichten, die einen permanenten Hintergrund benötigen).

Ich habe einen protokollorientierten Ansatz verwendet, mit einem BackgroundLockable-Protokoll, das die Liste der Ansichten enthält, die gesperrt werden sollen, und ein Schließen ausgeführt wird, während die Farben beibehalten werden: 

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

Dann habe ich eine Unterklasse von UITableViewCell, die das Hervorheben und die Auswahl überschreibt, um die Schließung des Protokolls auszuführen, um das Standardverhalten (super) aufzurufen:

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

Jetzt muss ich nur noch LockableBackgroundTableViewCell subclassieren oder das BackgroundLockable-Protokoll in einer Zellklasse verwenden, um einigen Zellen das Sperrverhalten leicht hinzuzufügen!

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]
    }
}
1
Yasir

Ich habe gerade etwas Zeit mit diesem seltsamen Thema verbracht. Ich wollte den UITableViewCellSelectionStyleNone-Stil nicht festlegen, um die Nice-Animation beizubehalten, wenn meine Zeile ausgewählt wurde. Aber keine der vorgeschlagenen Ideen hat sich für mich bewährt - ich habe versucht, setSelected und setHighlighted zu überschreiben und meine Hintergrundfarbe backgroundColor dort zu setzen - es wurde immer wieder von iOS zurückgesetzt und blinzelte immer noch (neue Farbe -> alte Farbe) recht einfach ... Wenn meine Zeile ausgewählt ist, wird ein anderer View-Controller gedrückt, der Benutzer wählt eine Option auf diesem Bildschirm aus, und der Delegat wird aufgerufen, wobei ich die Farbe aufgrund der Benutzerauswahl ändere. In diesem Delegierten mache ich einfach [cell setSelected: NO animated: NO] für meine Zelle. (Ich habe einen statischen UITableViewController und habe Auslässe zu Zellen.) . Sie könnten die Zelle in der didSelect-Methode wahrscheinlich abwählen, aber in meinem Fall verwende ich Segues.

0
dimaclopin

Hier ist meine Meinung dazu. Ich habe eine Unterklasse, von der alle meine Zellen erben. Auf diese Weise vermeide ich die Hintergrundänderung in meinen 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]
        }
    }
}

Dies behebt das Problem automatisch für alle UIImageView.

0
allaire

In iOS 7 funktionierte es für mich, setSelected:animated: in der UITableViewCell-Unterklasse zu überschreiben, aber im Gegensatz zu @Brooks 'Tipp rief ich [super setSelected:selected animated:animated] an.

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

Dadurch kann ich die Standardauswahlanimation des Systems beibehalten, wenn der Benutzer auf die Zelle tippt, und die Auswahl in der Tabelle didSelectRowAtIndexPath: des Delegierten der Tabellenansicht aufheben.

0
Matthew Quiros

Sie können das Verhalten von tableViewCell ändern, indem Sie die Funktion setHighlighted in der UITableViewCell-Klasse überschreiben (Sie müssen davon erben). Beispiel für meinen Code, bei dem ich das Hintergrundbild meiner Zelle ändere:

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

Sie können den Auswahlmodus im Interface-Builder auch auf grau, blau oder keinen ändern.

0
CedricSoubrie

Da Sie sagten, Sie hätten eine tableViewCell mit IB erstellt, möchte ich prüfen, ob Sie Ihre Ansicht als Unteransicht von contentView von UITableViewCell hinzufügen, nicht als view. Die Inhaltsansicht ist die Standardübersicht für den von der Zelle angezeigten Inhalt.

Aus der Referenz:

Die Inhaltsansicht eines UITableViewCell-Objekts ist der Standardüberblick für den von der Zelle angezeigten Inhalt. Wenn Sie Zellen anpassen möchten, indem Sie einfach zusätzliche Ansichten hinzufügen, sollten Sie sie zur Inhaltsansicht hinzufügen, damit sie beim Übergang der Zelle in den Bearbeitungsmodus bzw. aus dem Bearbeitungsmodus entsprechend positioniert werden.

0
MHC

Swift 4

In Ihrer UITableViewCell -Klasse:

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

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