Subview UITableViewCell menghilang saat sel dipilih

178

Saya menerapkan tampilan tabel pemilih warna di mana pengguna dapat memilih di antara, katakanlah, 10 warna (tergantung pada produk). Pengguna juga dapat memilih opsi lain (seperti kapasitas hard drive, ...).

Semua pilihan warna ada di dalam bagian tampilan tabel mereka sendiri.

Saya ingin menampilkan kotak kecil di sebelah kiri textLabel yang menunjukkan warna sebenarnya.

Saat ini saya sedang menambahkan kotak UIView sederhana, berikan warna latar yang benar, seperti ini:

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

Ini menampilkan baik-baik saja tanpa masalah.

Satu-satunya masalah saya adalah ketika saya memilih baris "warna", selama animasi pemilihan fade-to-blue, UIView khusus saya (colorThumb) disembunyikan. Ini akan terlihat lagi tepat setelah animasi pemilihan / deseleksi berakhir, tetapi ini menghasilkan artefak yang jelek.

Apa yang harus saya lakukan untuk memperbaikinya? Bukankah saya harus memasukkan subview di tempat yang tepat?

(Tidak ada yang istimewa di didSelectRowAtIndexPath, saya hanya mengganti aksesori sel ke kotak centang atau tidak sama sekali, dan batal memilih indexPath saat ini).

Cyrille
sumber
Apa upadteCell semua tentang?
Idan
updateCell melakukan beberapa perubahan kecil seperti mengatur tanda centang atau tidak, memilih warna teks tergantung pada ketersediaan, ... tetapi tidak ada perubahan nyata terkait dengan sel itu sendiri atau colorThumb.
Cyrille
jawaban yang diterima tidak memberikan solusinya, lihat jawaban saya di bawah ini untuk solusinya
Pavel Gurov

Jawaban:

227

UITableViewCellmengubah warna latar belakang dari semua sub pandangan ketika sel yang dipilih atau disorot, Anda dapat Memecahkan masalah ini dengan menimpa Tableview sel setSelected:animateddan setHighlighted:animateddan ulang warna tampilan background.

Dalam Tujuan 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;
    }
}

Dalam 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
sumber
Apakah kita memerlukan if (highlighted)dan if (selected)ketentuannya? Saya pikir itu akan berhasil jika kita tidak memiliki kondisi itu.
Rishabh Tayal
@RishabhTayal pada dasarnya adalah untuk menghindari mengesampingkan variabel dengan nilai yang sama
cameloper
1
Ingatlah bahwa ketika Anda mengubah warna latar belakang saat item dipilih, warna lama (salah) mungkin dipulihkan saat item menjadi tidak dipilih. Jika itu bisa terjadi, hapus kondisi if (disorot) dan if (terpilih).
Marcel W
Pendekatan ini membatalkan animasi, sehingga tidak masuk akal. Lebih baik mengatur gaya pemilihan sel ke .none.
Alexander Danilov
122

Itu karena sel tampilan tabel secara otomatis mengubah warna latar belakang semua tampilan di dalam tampilan konten untuk keadaan yang disorot. Anda dapat mempertimbangkan subkelas UIViewuntuk menggambar warna Anda atau menggunakan UIImageViewdengan gambar peregangan 1x1 px khusus.

Andriy
sumber
1
Bodoh aku. Tentu saja itu saja, subview harus transparan sehingga pemilihan animasi dapat terjadi dengan benar. Terima kasih!
Cyrille
52
Atau Anda dapat mengatur ulang overriding warna latar belakang setHighlighted:animated:dansetSelected:animated:
pt2ph8
1
Entah bagaimana, mengatur ulang warna latar tidak bekerja untuk saya (berjalan di iOS 8.1). Alih-alih menyelesaikan ini dengan mensubklasifikasikan pandangan saya dan mengesampingkan setBackgroundColor sebagai [super setBackgroundColor: [UIColor whiteColor]].
bizz84
44

Menemukan solusi yang cukup elegan alih-alih mengacaukan metode seleksi / penyorotan tableViewCell. Anda dapat membuat subkelas UIView yang mengabaikan pengaturan warna latar belakangnya menjadi warna yang jelas.

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

Versi obj-C:

@interface NeverClearView : UIView

@end

@implementation NeverClearView

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

@end
Pavel Gurov
sumber
Ini indah. Mudah solusi terbaik jika Anda memiliki tampilan yang dapat digunakan kembali seperti "lencana" atau "tag" yang seharusnya tidak pernah memiliki latar belakang yang jelas. :: rant :: apa solusi yang membingungkan @UIKit, mengatur semua tampilan anak menjadi transparan saat Anda melakukan pemilihan sel. Paling tidak batas untuk tampilan anak yang tinggi atau lebar sel penuh, atau yang pada kedalaman N.
SimplGy
@SimplGy Kedalaman penyorotan memang akan menjadi pilihan yang manis, tapi hei - ini UIKit, saya telah melihat banyak hal yang jauh lebih buruk dari ini =)
Pavel Gurov
3
Bekerja untuk saya setelah saya mengubah if ke CGColorGetAlpha (backgroundColor! .CGColor) == 0 karena tidak sama dengan clearColor
piltdownman7
9

Cara lain untuk mengelola masalah adalah mengisi tampilan dengan gradien inti-grafis, seperti:

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
sumber
Hmm, saya mencoba kode persis ini dan tidak ada efek sama sekali bagi saya. Subview saya adalah UILabel yang ditambahkan sebagai subview dari cell.contentView, dan pengujian di bawah iOS 6.0.1, jika hal itu penting.
Joe Strout
Apa yang Anda terapkan kode di atas? Dan sudahkah Anda mencoba menambahkan label hanya ke tampilan sel?
Agat
Ini adalah solusi sempurna menurut saya. Menggambar ke lapisan mengatasi masalah dengan sempurna, sambil menjaga fleksibilitas penuh. Saya tidak suka solusi menggunakan UIImageView, karena dengan itu lebih sulit untuk menyesuaikan gradien atau warna (Anda harus membuat gambar baru setiap kali), dan subklasifikasi UIView hanya untuk ini sepertinya berlebihan.
Erik van der Neut
@Lyida, saya bukan pengembang Swift. (Saya bahkan lebih C # -satu). Tapi dari apa yang saya lihat, itu bukan hal yang agak spesifik bahasa, bagaimanapun, sebagian besar Cocoa / iOS Frameworks satu. Jadi, idenya adalah hanya menempatkan CAGradientLayer yang hampir transparan ke dalam tampilan Anda untuk mendapatkan hasil yang diminta.
Agat
9

Untuk Swift 2.2 ini berfungsi

cell.selectionStyle = UITableViewCellSelectionStyle.None

dan alasan dijelaskan oleh @ Andriy

Itu karena sel tampilan tabel secara otomatis mengubah warna latar belakang dari semua tampilan di dalam tampilan konten untuk keadaan yang disorot.

swiftBoy
sumber
8

Terinspirasi oleh Yatheesha BL 's jawaban saya membuat sebuah UITableViewCell kategori / ekstensi yang memungkinkan Anda untuk menghidupkan dan mematikan transparansi ini 'fitur'.

Cepat

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

Objektif-C

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

KeepBackgroundCell kompatibel dengan CocoaPods. Anda dapat menemukannya di GitHub

Tim Bodeit
sumber
7

Anda bisa cell.selectionStyle = UITableViewCellSelectionStyleNone;, lalu atur backgroundColor di- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath

gumpwang
sumber
4

Terinspirasi oleh jawaban Yatheesha BL .

Jika Anda memanggil super.setSelected (terpilih, animasi: animasi), itu akan menghapus semua warna latar yang Anda atur . Jadi, kami tidak akan memanggil metode super.

Dalam 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
sumber
1
Terima kasih atas solusinya. +1 Meng-override variabel ishiglighted dan metode sethighlighted adalah hal yang berbeda. jawaban yang benar adalah mengganti metode.
Numan Karaaslan
4

Untuk kasus, Ini adalah Septs untuk menghindari mendapatkan warna abu-abu untuk semua item dalam sel (jika Anda menggunakan sel tampilan tabel kustom):

  1. Setel selectionStyle ke .none 👉 selectionStyle = .none

  2. Ganti metode ini.

    func setHighlighted (_ disorot: Bool, animated: Bool)

  3. Panggil super, untuk mendapatkan manfaat dari pengaturan super.

    super.setHighlighted (disorot, beranimasi: beranimasi)

  4. Lakukan apa yang pernah menyoroti logika yang Anda inginkan.

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

UITableViewCell mengubah backgroundColor semua subview pada pilihan karena beberapa alasan.

Ini mungkin membantu:

DVColorLockView

Gunakan sesuatu seperti itu untuk menghentikan UITableView mengubah warna tampilan Anda selama pemilihan.

DylanVann
sumber
1

Gambarkan tampilan alih-alih mengatur warna latar belakang

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
sumber
1

Solusi SIMPLEST tanpa bug dengan animasi (seperti pada jawaban teratas) dan tanpa subkelas dan gambar - atur warna batas layer alih-alih warna latar belakang dan atur lebar batas yang sangat besar.

colorThumb.layer.cornerRadius = 6
colorThumb.layer.borderWidth = colorThumb.frame.width
colorThumb.layer.borderColor = value.color
Alexander Danilov
sumber
0

Coba kode berikut:

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

Jangan lupa untuk menimpanya setSelectedjugasetHighlighted

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
sumber
0

Ini mirip dengan jawaban Pavel Gurov, tetapi lebih fleksibel karena memungkinkan warna apa pun menjadi permanen.

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

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

Saya ingin mempertahankan perilaku pemilihan default kecuali satu subview sel yang ingin saya abaikan perubahan warna latar belakang otomatis. Tapi saya juga harus bisa mengubah warna latar di waktu lain.

Solusi yang saya buat adalah dengan subclass UIViewsehingga mengabaikan pengaturan warna latar belakang secara normal dan menambahkan fungsi terpisah untuk memintas perlindungan.

Cepat 4

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

    override var backgroundColor: UIColor? {
        set {
            return
        }
        get {
            return super.backgroundColor
        }
    }
}
zekel
sumber
-1

di sini adalah solusi saya, gunakan contentView untuk menunjukkan selectionColor, ini berfungsi dengan baik

#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;
    }
}
Nick
sumber
-1

Tempatkan kode ini di subclass Anda dari UITableViewCell

Sintaks 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
sumber
-1

Menambahkan solusi lain jika Anda menggunakan storyboard. Buat subkelas UIViewyang tidak memungkinkan backgroundColoruntuk diatur setelah awalnya ditetapkan.

@interface ConstBackgroundColorView : UIView

@end

@implementation ConstBackgroundColorView

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

@end
mofojed
sumber
-1

Jika solusi latar belakang yang disebutkan di atas tidak memperbaiki masalah Anda, masalah Anda mungkin terletak pada Anda datasource untuk tableView Anda.

Bagi saya, saya sedang membuat turunan dari objek DataSource (dipanggil BoxDataSource) untuk menangani metode delegasi dan tableSource dataView, seperti:

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

Ini menyebabkan dataSource untuk tidak dialokasikan setiap kali sel disadap, dan dengan demikian semua konten menghilang. Alasannya, adalah ARC deallocating / sifat pengumpulan sampah.

Untuk memperbaikinya, saya harus masuk ke sel khusus, menambahkan variabel sumber data:

//CustomCell.swift
var dataSource: BoxDataSource?

Kemudian, Anda perlu mengatur dataSource ke dataSource sel yang varbaru saja Anda buat di cellForRow, jadi ini tidak dapat dialokasikan dengan ARC.

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

Semoga itu bisa membantu.

Josh O'Connor
sumber