Подвид UITableViewCell исчезает при выделении ячейки


178

Я реализую табличное представление с выбором цвета, где пользователь может выбрать, скажем, 10 цветов (зависит от продукта). Пользователь также может выбрать другие параметры (например, емкость жесткого диска, ...).

Все варианты цвета находятся в их собственном разделе таблицы.

Я хочу отобразить небольшой квадрат слева от textLabel, показывающий фактический цвет.

Прямо сейчас я добавляю простой квадратный UIView, присваиваю ему правильный цвет фона, например так:

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

Это нормально отображается без проблем.

Моя единственная проблема заключается в том, что когда я выбираю «цветную» строку, во время анимации выделения с переходом в синий цвет, мой пользовательский UIView (colorThumb) скрывается. Он снова становится видимым сразу после окончания анимации выбора / отмены выбора, но это приводит к появлению уродливого артефакта.

Что я должен сделать, чтобы исправить это? Разве я не вставляю подпредставление в нужном месте?

(В didSelectRowAtIndexPath нет ничего особенного, я просто изменяю принадлежность ячейки на флажок или ничего, и отменяю выбор текущего indexPath).


Что такое upadteCell?
Идан

updateCell делает некоторые незначительные изменения, такие как установка галочки или нет, выбор цвета текста в зависимости от доступности, ... но никаких реальных изменений, связанных с самой ячейкой или colorThumb.
Сирил

принятый ответ не дает решения, см. мой ответ ниже для решения
Павел Гуров

Ответы:


227

UITableViewCellизменяет цвет фона всех вложенных представлений, когда ячейка выделена или выделена. Вы можете решить эту проблему, переопределив ячейки Tableview setSelected:animatedиsetHighlighted:animated сбросив цвет фона представления.

В Задаче 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;
    }
}

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

Нужно ли нам в if (highlighted)и if (selected)условии? Я думаю, что это будет работать, если у нас нет таких условий.
Ришабх Тайал

@RishabhTayal это в основном, чтобы избежать переопределения переменной с тем же значением
cameloper

1
Помните, что при изменении цвета фона при выборе элемента старый (неправильный) цвет может быть восстановлен, когда элемент становится невыбранным. Если это может произойти, удалите условия if (выделено) и if (выбрано).
Марсель W

Такой подход отменяет анимацию, поэтому он не имеет смысла. Лучше установить стиль выбора ячейки на .none.
Александр Данилов

122

Это связано с тем, что ячейка табличного представления автоматически меняет цвет фона всех представлений внутри представления содержимого для выделенного состояния. Вы можете рассмотреть возможность создания подкласса UIViewдля рисования вашего цвета или использования UIImageViewрастянутого изображения размером 1x1 px.


1
Тупой меня Конечно, это так, подпредставления должны быть прозрачными, чтобы анимация выделения могла происходить правильно. Спасибо!
Сирил

52
Или вы можете переустановить переопределение цвета фона setHighlighted:animated:иsetSelected:animated:
pt2ph8

1
Каким-то образом сброс цвета фона у меня не сработал (работает на iOS 8.1). Вместо этого решил это путем создания подкласса моего представления и переопределения setBackgroundColor как [super setBackgroundColor: [UIColor whiteColor]].
bizz84

44

Нашел довольно элегантное решение вместо того, чтобы возиться с методами выделения / подсветки tableViewCell. Вы можете создать подкласс UIView, который игнорирует установку его цвета фона, чтобы очистить цвет.

Свифт 3/4:

class NeverClearView: UIView {
    override var backgroundColor: UIColor? {
        didSet {
            if backgroundColor != nil && backgroundColor!.cgColor.alpha == 0 {
                backgroundColor = oldValue
            }
        }
    }
}

Свифт 2:

class NeverClearView: UIView {
    override var backgroundColor: UIColor? {
        didSet {
            if CGColorGetAlpha(backgroundColor!.CGColor) != 0 {
                backgroundColor = oldValue
            }
        }
    }
}

Версия Obj-C:

@interface NeverClearView : UIView

@end

@implementation NeverClearView

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

@end

Это прекрасно. Легко лучшее решение, если у вас есть какое-то многократно используемое представление, например, «значок» или «тег», которые никогда не должны иметь четкого фона. :: rant :: что за смешное решение @UIKit, устанавливающее прозрачность всех дочерних представлений при выборе ячейки. По крайней мере, ограничить дочерние представления, которые являются полной высотой или шириной ячейки, или те, что на N глубине.
SimplGy

@SimplGy Глубина выделения была бы действительно приятным вариантом, но эй - это UIKit, я видел вещи НАСТОЛЬКО хуже, чем это =)
Павел Гуров

3
Сработал для меня после того, как я изменил if на CGColorGetAlpha (backgroundColor! .CGColor) == 0, так как он не был равен clearColor
piltdownman7

9

Другой способ справиться с этой проблемой - заполнить вид графическим ядром, например:

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

Хм, я пытаюсь этот точный код, и он не имеет никакого эффекта для меня. Мое подпредставление - UILabel, добавленное как подпредставление cell.contentView, и тестирование под iOS 6.0.1, в случае необходимости.
Джо Струт

К чему вы применяете код выше? И вы пытались добавить метку просто в вид ячейки?
Агат

Это идеальное решение на мой взгляд. Рисование на слое решает проблему идеально, сохраняя полную гибкость. Мне не нравится решение использовать UIImageView, потому что тогда будет сложнее настроить градиент или цвет (вам придется каждый раз создавать новое изображение), и подкласс UIView только для этого кажется излишним.
Эрик ван дер Нойт

@Lyida, я не очень Swift-разработчик. (Я даже больше C # -one). Но из того, что я видел, это не совсем зависит от языка, в основном это логика Cocoa / iOS Frameworks. Итак, идея состоит в том, чтобы просто разместить почти прозрачный CAGradientLayer в вашем представлении, чтобы получить запрошенный результат.
Агат

9

Для Swift 2.2 это работает

cell.selectionStyle = UITableViewCellSelectionStyle.None

и причина объясняется @ Andriy

Это связано с тем, что ячейка табличного представления автоматически меняет цвет фона всех представлений внутри представления содержимого для выделенного состояния.


8

Вдохновленный Yatheesha BL «s ответ я создал UITableViewCell категории / расширение , которое позволяет включать и выключать эту функцию прозрачности„“.

стриж

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 совместим с CocoaPods. Вы можете найти его на GitHub


7

Вы можете cell.selectionStyle = UITableViewCellSelectionStyleNone;затем установить backgroundColor на- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath


4

Вдохновленный Yatheesha BL .

Если вы вызовете super.setSelected (selected, animated: animated), он очистит весь заданный вами цвет фона . Итак, мы не будем называть супер метод.

В Свифте:

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
Спасибо за решение. +1 Переопределение переменной с подсветкой и метода с подсветкой - это разные вещи. правильный ответ - переопределить метод.
Нуман Карааслан

4

В этом случае это Septs, чтобы избежать получения серого цвета для всех элементов в ячейке (если вы используете настраиваемую ячейку табличного представления):

  1. Установите для selectionStyle значение .none 👉 selectionStyle = .none

  2. Переопределите этот метод.

    func setHighlighted (_ выделено: Bool, анимировано: Bool)

  3. Вызовите супер, чтобы воспользоваться преимуществами супер-настройки.

    super.setHighlighted (выделено, анимировано: анимировано)

  4. Делайте то, что когда-либо выделяя логику, которую вы хотите.

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

3

UITableViewCell по какой-то причине изменяет backgroundColor всех подпредставлений при выделении.

Это может помочь:

DVColorLockView

Используйте что-то подобное, чтобы UITableView не менял цвет вашего вида во время выделения.


1

Нарисуйте вид вместо установки цвета фона

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

SIMPLEST решение без ошибок с анимацией (как в ответе с самым высоким рейтингом) и без подклассов и рисования - установите цвет границы слоя вместо backgroundColor и установите очень большую ширину границы.

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

0

Попробуйте следующий код:

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

0

Не забудьте переопределить, setSelectedа также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()
}

0

Это похоже на ответ Павла Гурова, но более гибко в том смысле, что позволяет любому цвету быть постоянным.

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

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

0

Я хотел сохранить поведение выбора по умолчанию, за исключением одного подпредставления ячейки, которое я хотел игнорировать автоматическое изменение цвета фона. Но мне также нужно было иметь возможность менять цвет фона в другое время.

Решение, которое я придумал, состояло в том, UIViewчтобы создать подкласс, чтобы он игнорировал обычную настройку цвета фона и добавил отдельную функцию для обхода защиты.

Swift 4

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

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

-1

вот мое решение, используйте contentView, чтобы показать selectionColor, это отлично работает

#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

Поместите этот код в свой подкласс UITableViewCell

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

Добавление другого решения, если вы используете раскадровки. Создайте подкласс UIView, который не позволяет backgroundColorбыть установленным после того, как это первоначально установлено.

@interface ConstBackgroundColorView : UIView

@end

@implementation ConstBackgroundColorView

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

@end

-1

Если вышеупомянутое фоновое решение не решает вашу проблему, ваша проблема может лежать в вашей datasource вашей таблице.

Для меня я создавал экземпляр объекта DataSource (вызываемый BoxDataSource) для обработки методов tableView делегата и dataSource следующим образом:

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

Это вызывало освобождение источника данных при каждом касании ячейки, и, таким образом, все содержимое исчезало. Причина в том, что ARC освобождает / собирает мусор.

Чтобы это исправить, мне нужно было перейти в пользовательскую ячейку и добавить переменную источника данных:

//CustomCell.swift
var dataSource: BoxDataSource?

Затем вам нужно установить dataSource на источник данных ячейки, который varвы только что создали в cellForRow, так что это не освобождается от ARC.

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

Надеюсь, это поможет.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.