Widok podrzędny UITableViewCell znika po wybraniu komórki

178

Wdrażam widok tabeli wyboru kolorów, w którym użytkownik może wybrać, powiedzmy, 10 kolorów (w zależności od produktu). Użytkownik może również wybrać inne opcje (takie jak pojemność dysku twardego, ...).

Wszystkie opcje kolorów znajdują się w osobnej sekcji widoku tabeli.

Chcę wyświetlić mały kwadrat po lewej stronie textLabel pokazujący rzeczywisty kolor.

W tej chwili dodaję prosty kwadratowy UIView, nadaj mu poprawny kolor tła, taki jak ten:

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

Wyświetla się dobrze bez problemów.

Moim jedynym problemem jest to, że kiedy wybieram wiersz „kolorowy”, podczas animacji wyboru przejścia do niebieskiego moje niestandardowe UIView (colorThumb) jest ukryte. Ponownie staje się widoczny tuż po zakończeniu animacji wyboru / odznaczenia, ale powoduje to brzydki artefakt.

Co powinienem zrobić, aby to naprawić? Czy nie wstawiam podview we właściwym miejscu?

(W didSelectRowAtIndexPath nie ma nic specjalnego, po prostu zmieniam akcesorium komórki na pole wyboru lub nic i odznaczam bieżącą ścieżkę indexPath).

Cyryl
źródło
O co chodzi w upadteCell?
Idan,
updateCell wprowadza pewne drobne poprawki, takie jak ustawienie znacznika wyboru lub nie, wybór koloru tekstu w zależności od dostępności, ... ale brak rzeczywistych zmian związanych z samą komórką lub colorThumb.
Cyrille
zaakceptowana odpowiedź nie zapewnia rozwiązania, zobacz moją odpowiedź poniżej
Pavel Gurov

Odpowiedzi:

227

UITableViewCellzmienia kolor tła wszystkich widoków podrzędnych, gdy komórka jest zaznaczona lub podświetlone, można rozwiązać ten problem poprzez nadpisanie komórka Tableview użytkownika setSelected:animatedi setHighlighted:animatedi resetowanie kolor tła widoku.

W celu 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;
    }
}

W 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
źródło
Czy potrzebujemy if (highlighted)i if (selected)warunki? Myślę, że to zadziała, jeśli nie mamy takich warunków.
Rishabh Tayal
@RishabhTayal w zasadzie należy unikać zastępowania zmiennej o tej samej wartości
wielbłąd
1
Pamiętaj, że jeśli zmienisz kolor tła, gdy element jest zaznaczony, stary (zły) kolor może zostać przywrócony, gdy element nie zostanie zaznaczony. Jeśli tak się stanie, usuń warunki if (podświetlone) i if (wybrane).
Marcel W
Takie podejście anuluje animację, więc nie ma sensu. Lepiej ustawić styl zaznaczania komórek na .none.
Alexander Danilov
122

Jest tak, ponieważ komórka widoku tabeli automatycznie zmienia kolor tła wszystkich widoków w widoku zawartości dla podświetlonego stanu. Możesz rozważyć podklasę, UIViewaby narysować kolor lub użyć UIImageViewniestandardowego rozciągniętego obrazu 1x1 px.

Andrij
źródło
1
Ogłup mnie. Oczywiście o to chodzi, widoki podrzędne muszą być przezroczyste, aby animacja selekcji mogła przebiegać poprawnie. Dzięki!
Cyrille
52
Lub możesz ponownie ustawić setHighlighted:animated:setSelected:animated:
przesłonięcie
1
Jakoś zresetowanie koloru tła nie działało dla mnie (działające na iOS 8.1). Zamiast tego rozwiązałem to, podklasując mój widok i nadpisując setBackgroundColor jako [super setBackgroundColor: [UIColor whiteColor]].
bizz84
44

Znaleziono dość eleganckie rozwiązanie zamiast zadzierać z metodami wyboru / podświetlania tableViewCell. Możesz utworzyć podklasę UIView, która ignoruje ustawianie koloru tła na czysty kolor.

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

Wersja Obj-C:

@interface NeverClearView : UIView

@end

@implementation NeverClearView

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

@end
Pavel Gurov
źródło
To jest urocze. Łatwo najlepsze rozwiązanie, jeśli masz widok wielokrotnego użytku, taki jak „znaczek” lub „znacznik”, który nigdy nie powinien mieć wyraźnego tła. :: rant :: co za mylące rozwiązanie @UIKit, ustawiając wszystkie widoki potomne na przezroczyste podczas dokonywania wyboru komórki. Przynajmniej ograniczenie do widoków podrzędnych, które są pełnej wysokości lub szerokości komórki, lub tych na głębokości N.
SimplGy
@SimplGy Głębokość podświetlania byłaby naprawdę słodką opcją, ale hej - to jest UIKit, widziałem rzeczy O wiele gorsze niż to =)
Pavel Gurov
3
Pracowałem dla mnie po tym, jak zmieniłem if na CGColorGetAlpha (backgroundColor! .CGColor) == 0, ponieważ nie było to równe clearColor
piltdownman7
9

Innym sposobem na rozwiązanie problemu jest wypełnienie widoku gradientem rdzenia-grafiki, takim jak:

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
źródło
Hmm, próbuję dokładnie tego kodu i nie ma to dla mnie żadnego efektu. Mój widok podrzędny to UILabel dodany jako widok podrzędny cell.contentView i testowanie pod iOS 6.0.1, w razie potrzeby.
Joe Strout
Do czego stosujesz powyższy kod? Czy próbowałeś dodać etykietę po prostu do widoku komórki?
Agat
To moim zdaniem idealne rozwiązanie. Rysowanie na warstwie doskonale rozwiązuje ten problem, zachowując jednocześnie pełną elastyczność. Nie podoba mi się rozwiązanie polegające na użyciu UIImageView, ponieważ wtedy trudniej jest dopasować gradient lub kolor (trzeba za każdym razem tworzyć nowy obraz), a podklasowanie UIView tylko z tego powodu wydaje się przesadą.
Erik van der Neut
@Lyida, nie jestem tak naprawdę twórcą Swift. (Mam jeszcze więcej C # -one). Jednak z tego, co widziałem, nie jest to raczej rzecz specyficzna dla języka, głównie logika Cocoa / iOS Frameworks. Pomysł polega więc na umieszczeniu prawie przezroczystej warstwy CAGradientLayer w widoku, aby uzyskać żądany wynik.
Agat
9

W przypadku Swift 2.2 to działa

cell.selectionStyle = UITableViewCellSelectionStyle.None

a powód wyjaśniono przez @ Andriy

Jest tak, ponieważ komórka widoku tabeli automatycznie zmienia kolor tła wszystkich widoków w widoku zawartości dla podświetlonego stanu.

swiftBoy
źródło
8

Zainspirowany Yatheesha BL „s odpowiedzi I stworzył UITableViewCell kategoria / rozszerzenie, które pozwala na włączanie i wyłączanie tej funkcji«przejrzystości».

Szybki

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

Cel C

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

KeepBackgroundCell jest kompatybilny z CocoaPods. Możesz go znaleźć na GitHub

Tim Bodeit
źródło
7

Możesz cell.selectionStyle = UITableViewCellSelectionStyleNone;, a następnie ustawić kolor tła na- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath

gumpwang
źródło
4

Zainspirowany odpowiedzią Yatheesha BL .

Wywołanie super.setSelected (wybrane, animowane: animowane) spowoduje wyczyszczenie wszystkich ustawionych kolorów tła . Nie będziemy więc wywoływać metody super.

W 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
źródło
1
Dzięki za rozwiązanie. +1 Przesłanianie zmiennej ishiglighted i metody sethighlighted to różne rzeczy. poprawną odpowiedzią jest zastąpienie metody.
Numan Karaaslan
4

W przypadku majowym jest to Septs, aby uniknąć uzyskania szarego koloru dla wszystkich elementów w komórce (w przypadku korzystania z niestandardowej komórki widoku tabeli):

  1. Ustaw opcję selectionStyle na .none 👉 selectionStyle = .none

  2. Zastąp tę metodę.

    func setHighlighted (_ podświetlony: Bool, animowany: Bool)

  3. Zadzwoń do super, aby skorzystać z superkonfiguracji.

    super.setHighlighted (podświetlony, animowany: animowany)

  4. Rób, co chcesz, podkreślając logikę.

    override func setHighlighted(_ highlighted: Bool, animated: Bool) {
          super.setHighlighted(highlighted, animated: animated)
          // Your Highlighting Logic goes here...
    }
Abo3atef
źródło
3

UITableViewCell z jakiegoś powodu zmienia kolor tła wszystkich widoków podrzędnych przy wyborze.

To może pomóc:

DVColorLockView

Użyj czegoś takiego, aby powstrzymać UITableView przed zmianą koloru widoku podczas wyboru.

DylanVann
źródło
1

Narysuj widok zamiast ustawiać kolor tła

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
źródło
1

NAJPROSTSZE rozwiązanie bez błędów z animacją (jak w najwyżej ocenianej odpowiedzi) oraz bez podklas i rysowania - ustaw kolor obramowania warstwy zamiast tła Kolor i ustaw bardzo dużą szerokość obramowania.

colorThumb.layer.cornerRadius = 6
colorThumb.layer.borderWidth = colorThumb.frame.width
colorThumb.layer.borderColor = value.color
Aleksander Daniłow
źródło
0

Wypróbuj następujący kod:

-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{     
[super setHighlighted:highlighted animated:animated];
//Set your View's Color here.
}
Mehul Thakkar
źródło
0

Nie zapomnij, aby zastąpić setSelected, jak równieżsetHighlighted

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
źródło
0

Jest to podobne do odpowiedzi Pavla Gurova, ale bardziej elastyczne, ponieważ pozwala na utrwalenie dowolnego koloru.

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

    override var backgroundColor: UIColor? {
        didSet {
            if backgroundColor != permanentBackgroundColor {
                backgroundColor = permanentBackgroundColor
            }
        }
    }
}
użytkownik3352495
źródło
0

Chciałem zachować domyślne zachowanie zaznaczania, z wyjątkiem jednego widoku podrzędnego komórki, który chciałem zignorować automatyczną zmianę koloru tła. Ale musiałem też móc zmienić kolor tła w innym czasie.

Rozwiązaniem, które wymyśliłem, było podklasowanie, UIViewwięc ignoruje normalne ustawianie koloru tła i dodaje osobną funkcję, aby ominąć ochronę.

Szybki 4

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

    override var backgroundColor: UIColor? {
        set {
            return
        }
        get {
            return super.backgroundColor
        }
    }
}
zekel
źródło
-1

oto moje rozwiązanie, użyj contentView, aby pokazać wybórKolor, działa idealnie

#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;
    }
}
Nacięcie
źródło
-1

Umieść ten kod w swojej podklasie UITableViewCell

Składnia 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
źródło
-1

Dodanie innego rozwiązania, jeśli używasz scenariuszy. Utworzenie podklasy UIViewtego nie pozwala na backgroundColorustawienie po początkowym ustawieniu.

@interface ConstBackgroundColorView : UIView

@end

@implementation ConstBackgroundColorView

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

@end
mofojed
źródło
-1

Jeśli powyższe rozwiązanie w tle nie rozwiązuje problemu, problem może leżeć datasourcepo Twojej stronie tableView.

Dla mnie BoxDataSourcetworzyłem instancję obiektu DataSource (wywoływanego ) do obsługi metod delegowania i dataSource tableView, ponieważ:

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

Powodowało to zwolnienie przydziału dataSource za każdym razem, gdy komórka została dotknięta, a zatem cała zawartość zniknęła. Powodem jest dealokacja / zbieranie śmieci przez ARC.

Aby to naprawić, musiałem przejść do niestandardowej komórki, dodać zmienną źródła danych:

//CustomCell.swift
var dataSource: BoxDataSource?

Następnie musisz ustawić źródło danych na źródło danych komórki, varktóre właśnie utworzyłeś w cellForRow, aby nie zostało to zwolnione za pomocą ARC.

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

Mam nadzieję, że to pomaga.

Josh O'Connor
źródło