UIView backgroundColor исчезает при выборе UITableViewCell

У меня есть простая конструкция tableViewCell в построителе интерфейса. Он содержит UIView, который содержит изображение. Теперь, когда я выбираю ячейку, отображается синий фон выбора по умолчанию, но backgroundColor моего UIView ушел.

Мой файл реализации UITableViewCell не делает ничего особенного. Он просто инициализирует и возвращает self, и все, что я делаю в setSelected, - это вызов super.

Как получить мой UIView backgroundColor для отображения при выборе tableView?

Ответ 1

Проблема здесь в том, что реализация [super]

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

устанавливает все цвета фона в UITableViewCell в rgba (0,0,0,0). Зачем? Может быть, мы все потеем?

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

Вот последовательность вызовов функций, которая возникает при касании ячейки

  • setHighlighted
  • touchesEnded
  • layoutSubviews
  • willSelectRowAtIndexPath (сторона делегата)
  • setSelected (!!! здесь говорят, что все цвета фона вашего представления исчезают)
  • didSelectRowAtIndexPath (сторона делегата)
  • setSelected (снова) (Интересно, что цвета фона не очищаются от этого вызова. Какая странность происходит внутри этого супер-метода?)
  • layoutSubviews (снова)

Итак, ваши варианты:

  • Переопределить - (void) setSelected: (BOOL) выбранный анимированный: (BOOL) анимированный; без вызова [super setSelected: выбранный анимированный: анимированный]. Это даст вам наиболее технически правильную реализацию, потому что: а) код завернут внутри подкласса UITableViewCell и b), потому что он вызывается только тогда, когда это необходимо (ну, если нужно, дважды, но, возможно, есть способ обойти это). Нижняя сторона - вам придется повторно реализовать все необходимые функции (в отличие от ненужных функций очистки цвета) setSelected. Теперь не спрашивайте, как правильно переопределить setSelected. Ваша догадка так же хороша, как и моя (будьте терпеливы, я отредактирую этот ответ, как только выясню).
  • Восстановить цвета фона в didSelectRowAtIndexPath. Это не так здорово, потому что он ставит код экземпляра вне экземпляра. У этого есть потенциал роста, который он вызывается только тогда, когда это необходимо, в отличие от...
  • Повторно подтвердите цвета фона в layoutSubviews. Это не очень хорошо, потому что layoutSubviews вызывается как A MILLION раз! Он вызывается каждый раз, когда таблица обновляется, каждый раз, когда она прокручивается, каждый раз, когда бабушка получает перм... как всерьез, миллион раз. Это означает, что существует много ненужных фоновых повторных утверждений и много дополнительных накладных расходов. С яркой стороны он помещает код в подкласс UITableViewCell, что приятно.

К сожалению, повторное утверждение цветов фона в setHighlighted ничего не делает, потому что setHighlighted вызывается до того, как все цвета фона будут установлены на [r: 0 b: 0 g: 0 a: 0] первым вызовом setSelected.

//TODO: Дайте отличное описание того, как переопределить setSelected (следите за обновлениями)

Ответ 2

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

Ответ 3

Раньше я делал как @P5ycH0 сказал (растянутое изображение 1x1), но после @Brooks я решил, что переопределяя -(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated в моей пользовательской реализации UITableViewCell и сбросив цвета фона после вызова [super setHighlighted:highlighted animated:animated];, сохраняет мои фоновые цвета когда ячейка выбрана/выделена

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

Ответ 4

Когда ваш UITableViewCell выбран, есть два состояния, вы должны обратить внимание на: Highlighted и Selected.

Таким образом, для сценариев, в которых у вас есть собственный класс ячейки, который является подклассом UITableViewCell, вы можете легко переопределить эти два метода, чтобы избежать этой ситуации (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
    }

}

Ответ 5

Брукс имеет отличное объяснение, почему это происходит, но я думаю, что у меня есть лучшее решение.

В вашем представлении переопределите setBackgroundColor: на тот цвет, который вы хотите. Наборщик по-прежнему будет вызываться, но будет указан только указанный вами цвет.

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

Ответ 6

Хорошо, потеря цвета фона класса UIView является нормальным поведением, когда оно находится в выбранной таблице viewview. Я не мог понять, как это предотвратить. Теперь я только что заменил UIView на UIImageView, содержащий растянутый белый пиксель 1x1. Уродливое imo, но оно работает.

Ответ 7

Вам необходимо переопределить следующие два метода в вашей настраиваемой ячейке:

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

Обратите внимание, что:

  • вы должны называть [super setSelected:animated:] и [super setHighlighted:animated:] в начале вашей пользовательской реализации или соответствовать методам;
  • вы должны установить UITableViewCellSelectionStyleNone selectionStyle для вашей пользовательской ячейки, чтобы отключить стиль по умолчанию UITableViewCell;

Вот пример реализации:

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

contentView является свойством UITableViewCell, которое появилось в iOS 7. Обратите внимание, что вы можете использовать собственное представление дочернего элемента или представления вместо него.

Ответ 8

Добавьте это в свой UITableViewCell

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

Ответ 9

Относительно ответа @Brooks, это то, что я сделал, чтобы заставить его работать в Swift и iOS8/iOS9.

  • Отмените setSelected и setHighlighted
  • Не называйте super
  • Очистите contentView.backgroundColor, потому что он не должен охватывать всю ширину ячейки (например, аксессуаров).
  • Используйте backgroundColor самой ячейки и установите ее соответствующим образом.

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

Ответ 10

Резюме

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


На основе ответа mientus я создал решение, которое позволяет указывать, какие представления должны содержать цвет фона.

Это позволяет другим объектам подкачки объектов удалять фон при выделении/выборе и является единственным решением, которое работает в нашем случае (два представления, требующие постоянного фона).

Я использовал ориентированный на протокол подход с протоколом BackgroundLockable, содержащим список видов для блокировки и запуская закрытие при сохранении цветов:

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

Затем у меня есть подкласс UITableViewCell, который переопределяет выделение и выбор для запуска закрытия протокола вокруг вызова поведения по умолчанию (супер):

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

Теперь мне просто нужно подклассом LockableBackgroundTableViewCell или использовать протокол BackgroundLockable в классе ячеек, чтобы легко добавлять поведение блокировки в некоторые ячейки!

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

Ответ 11

Swift 4

В вашем классе UITableViewCell:

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

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

Ответ 12

Из того, что вы сказали, что вы построили tableViewCell с использованием IB, я хотел бы проверить, добавляете ли вы свое представление в качестве подзадачи contentView UITableViewCell, а не view. Представление контента - это супервизор по умолчанию для содержимого, отображаемого ячейкой.

Из справки:

Представление содержимого объекта UITableViewCell представляет собой супервизор по умолчанию для содержимого, отображаемого ячейкой. Если вы хотите настроить ячейки, просто добавив дополнительные виды, вы должны добавить их в представление контента, чтобы они были соответствующим образом размещены, когда ячейка переходит в режим редактирования и выходит из него.

Ответ 13

Вы можете изменить поведение tableViewCell, переопределив функцию setHighlighted в классе UITableViewCell (вам нужно наследовать от нее). Пример моего кода, где я меняю фоновое изображение моей ячейки:

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

Вы также можете изменить режим выделения на серый, синий или ни один в построителе интерфейса.

Ответ 14

В iOS 7 мне удалось переопределить setSelected:animated: в подклассе UITableViewCell, но вопреки подсказке @Brooks я назвал [super setSelected:selected animated:animated].

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

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

Ответ 15

Просто потратил некоторое время на эту странную проблему. Я не хотел, чтобы стиль UITableViewCellSelectionStyleNone сохранял приятную анимацию, когда была выбрана моя строка. Но ни одна из предложенных идей не работала для меня - я пытался переопределить setSelected и setHighlighted и установить там свой subview backgroundColor - он сохранял сброс iOS и все еще мигал (новый цвет → старый цвет). Для меня исправление было довольно простым. Когда моя строка выбрана, другой контроллер просмотра нажат, пользователь выбирает на этом экране какую-то опцию, и делегат называется, где я меняю цвет на основе выбора пользователя. В этом деле я просто делаю [cell setSelected: NO animated: NO] для моей ячейки. (У меня есть статический UITableViewController и есть выходы для ячеек). Вероятно, вы можете отменить выбор ячейки в методе didSelect, но в моем случае я использую segues.

Ответ 16

Здесь я беру на себя это. У меня есть подкласс, на который наследуется вся моя ячейка, так что я делаю это, чтобы избежать изменения фона в моих 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]
        }
    }
}

Это автоматически исправить проблему для всех UIImageView.

Ответ 17

Эта проблема может (наконец-то) быть решена в iOS 13. Этот приятный абзац можно найти в примечаниях к выпуску iOS 13 beta 3.

Класс UITableViewCell больше не изменяет свойства backgroundColor или isOpaque для contentView и любых его подпредставлений, когда ячейки становятся выделенными или выделенными. Если вы устанавливаете непрозрачный backgroundColor для любых подпредставлений ячейки внутри (и в том числе) в ContentView, это может повлиять на внешний вид, когда ячейка будет выделена или выделена. Самый простой способ решить любые проблемы с вашими подпредставлениями - убедиться, что их backgroundColor имеет значение nil или clear, а их непрозрачное свойство - false. Однако при необходимости вы можете переопределить методы setHighlighted (: animated :) и setSelected (: animated :), чтобы вручную изменить эти свойства в ваших подпредставлениях при переходе к выделенным и выбранным состояниям или из них. (13955336)

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