UITableViewCell被选中后,UIView的背景色消失了

71

我有一个简单的tableViewCell界面构建在界面生成器中。它包含一个包含图像的UIView。

现在,当我选择该单元格时,默认的蓝色选定背景显示,但我的UIView的背景颜色消失了。

我的UITableViewCell实现文件没有做任何特殊处理。它只是初始化并返回self,在setSelected中,我只调用super。

如何使我的UIView背景颜色在选择tableView时显示?


视图中还有哪些界面元素会被单元格选择颜色覆盖? - hennes
目前它只是一张图片。它的目的是让这张图片看起来像一张照片。稍后,我还会在上面添加一个标签。 - Tycho Pandelaar
17个回答

107

这里的问题是 [super] 的实现方式

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

将 UITableViewCell 中所有背景颜色设置为 rgba(0,0,0,0)。为什么呢?可能是为了让我们出一身汗吧?

并不是整个视图都消失了(因为如果更改视图的层边框属性,它们会保留)。

以下是触摸单元格时导致的函数调用序列:

  1. setHighlighted
  2. touchesEnded
  3. layoutSubviews
  4. willSelectRowAtIndexPath(委托方)
  5. setSelected(!!!这是告诉所有视图背景颜色消失的地方)
  6. didSelectRowAtIndexPath(委托方)
  7. setSelected(再次)(有趣的是,此调用未清除背景颜色。超级方法内部发生了什么奇怪的事情?)
  8. layoutSubviews(再次)

因此,您有以下选项:

  1. 重写 - (void)setSelected:(BOOL)selected animated:(BOOL)animated; 而不调用 [super setSelected:selected animated:animated]。这将为您提供最技术上正确的实现,因为 a)代码包含在 UITableViewCell 子类内部,并且 b)仅在需要时调用(好吧,当需要时调用两次,但也许有一种方法可以避免这种情况)。缺点是您将不得不重新实现 setSelected 的所有必要函数(而不是不必要的颜色清除函数)。现在不要问我如何正确地重写 setSelected。目前您和我一样(耐心等待,我弄清楚后会编辑此答案)。
  2. didSelectRowAtIndexPath 中重新断言背景颜色。这不太好,因为它会将本应属于实例的代码放到实例外面。它的优点是仅在需要时调用,而不像...
  3. layoutSubviews 中重新断言背景颜色。这一点也不好,因为 layoutSubviews 被调用了无数次!每次刷新表格、滚动表格、你奶奶烫头发都会调用它...说真的,无数次。这意味着有很多不必要的背景再断言和很多额外的处理开销。但好处是把代码放到 UITableViewCell 子类内部,这很好。

不幸的是,在 setHighlighted 中重新断言背景颜色什么也没做,因为在第一次调用 setSelected 之前就会调用 setHighlighted。

// TODO: 提供如何正确重写 setSelected 的描述(敬请关注)


1
非常好的回复。很有道理!我会关注覆盖操作的;-) - Tycho Pandelaar
只是跟进一下。我已经尝试了很多,但仍然没有找到我所谓的干净、通用的解决方案来覆盖setSelected。如果我找到了,我会发布更多信息...祝编码愉快。 - Brooks
1
如果您在setSelected中设置UIView的背景颜色,则还应该在setHighlighted中设置它,否则可能会看到一些闪烁。 - nh32rg
10
至少在 iOS 7 上,你也可以将单元格的 selectionStyle 设置为 UITableViewCellSelectionStyleNone,这样它就不会清除背景。唯一的缺点是它会忽略 selectedBackgroundView 属性。 - Austin
有什么进展吗?已经过去8年了,我还在关注。 - Nicholas

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

1
很棒的解决方案。比所有子类和CALayer解决方案都要干净。 - Joel
谢谢!你的解决方案是最好的! - Insider
这对我来说是最有用的答案。我稍微修改了它以适应我的用例(多个带有多个锁定背景视图的单元格),但是为可行的解决方案鼓掌! - Yasir
有史以来最好的解决方案! - Yongqiang Zhou

17

当你的UITableViewCell被选中时,你需要关注两种状态:HighlightedSelected

因此,对于那些拥有自定义单元格类(是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
    }

}

最佳答案:Swift - Rukshan

16

之前我按照 @P5ycH0 的建议(拉伸1x1像素图像)完成了,但是在遵循 @Brooks 的建议后,我发现覆盖我的自定义UITableViewCell实现中的-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated方法,并在调用[super setHighlighted:highlighted animated:animated];之后重置背景颜色,可以在单元格被选择/高亮时保留我的背景颜色。

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

谢谢,我已经找了两天了。 - sanjeev
你节约了我的时间,非常感谢。 - mychar
我认为这个 setHighlighted 函数是用来在单元格被选中时添加自定义行为的,顺便说一句,做得很好。 - Wendy Liga

7
这个问题可能(最终)在iOS 13中得到解决。在iOS 13 beta 3的发布说明中发现了这段精彩的文字。
引用:
当单元格变为高亮或选定状态时,UITableViewCell类不再更改contentView及其任何子视图的backgroundColor或isOpaque属性。如果您在cell的contentView内(包括其中)的任何子视图上设置了不透明的backgroundColor,则在单元格变为高亮或选定状态时,子视图的外观可能会受到影响。解决任何与子视图相关的问题的最简单方法是确保它们的backgroundColor设置为nil或clear,并且它们的opaque属性为false。但是,如果需要,您可以重写setHighlighted(:animated:)和setSelected(:animated:)方法,在移动到或从高亮和选定状态时手动更改这些属性。 (13955336)

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


最终他们修复了这个奇怪的东西。 - Tycho Pandelaar
但是这会使 selectedBackgroundView 变得过时 :( 因为它可能会保持隐藏状态。 - Vilém Kurz

4

Brooks已经对为什么会发生这种情况做了很好的解释,但我认为我有更好的解决方案。

在您的子视图中,重写setBackgroundColor:到您想要的任何颜色。设置器仍将被调用,但只强制实施您指定的颜色。

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

3

好的,当UIView类在选定的tableviewcell中时,失去背景颜色是正常行为。我无法找到防止此情况发生的方法。现在,我只是将UIView替换为UIImageView,其中包含一个拉伸的1x1白像素。在我看来很丑,但它可以工作。


3

您需要在自定义单元格中覆盖下面两个方法:

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

请注意:
  • 在自定义实现或相应方法的开头,您应该调用[super setSelected:animated:][super setHighlighted:animated:];
  • 为了禁用默认的UITableViewCell样式,您应该为自定义单元格设置UITableViewCellSelectionStyleNone选择样式;
下面是实现示例:
- (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();
}

contentViewUITableViewCell 的一个属性,它出现在 iOS 7 中。请注意,您可以使用自己的单元格子视图或视图来代替它。


1

摘要

该解决方案使您能够锁定单元格的某些背景颜色,而其余部分由系统行为控制。


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

1

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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接