选择UITableViewCell时,UIView backgroundColor消失

时间:2011-03-07 17:11:26

标签: ios uitableview uiview interface-builder

我在界面构建器中有一个简单的tableViewCell构建。 它包含一个包含图像的UIView。 现在,当我选择单元格时,会显示默认的蓝色选择背景,但我的UIView的backgroundColor消失了。

我的UITableViewCell的实现文件没有做任何特别的事情。它只是初始和&返回self,我在setSelected中所做的就是调用super。

如何在选择tableView时显示我的UIView backgroundColor?

17 个答案:

答案 0 :(得分:106)

这里的问题是

的[超级]实现
- (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(再次)
  9. 所以你的选择是

    1. 覆盖 - (void)setSelected:(BOOL)选择动画:(BOOL)动画; 而不调用 [super setSelected:selected animated:animated] 。这将为您提供技术上最正确的实现,因为a)代码被包含在UITableViewCell子类中,并且b)因为它仅在需要时被调用(在需要时需要两次,但也许有办法解决)。缺点是你必须重新实现setSelected的所有必要功能(而不是不必要的颜色清除功能)。现在不要问我如何正确覆盖setSelected。你的猜测现在和我的一样好(耐心点,一旦我搞清楚,我就会编辑这个答案)。
    2. 重新断言 didSelectRowAtIndexPath 中的背景颜色。这不是很好,因为它将实例代码放在实例之外。它有一个好处,它只在需要时调用,而不是......
    3. layoutSubviews 中重新声明背景颜色。这根本不是很好,因为layoutSubviews被调用为数百万次!每次表格刷新时,每当你滚动时,每当你的祖母得到一个烫发时,它就会被调用......就像认真,一百万次一样。这意味着存在许多不必要的后台重新断言和大量额外的处理开销。好的一面是它将代码放在UITableViewCell子类中,这很不错。
    4. 不幸的是,在setHighlighted中重新断言背景颜色没有任何作用,因为在第一次调用setSelected所有背景颜色设置为[r:0 b:0 g:0 a:0]之前调用setHighlighted。

      // TODO:详细说明如何覆盖setSelected(敬请期待)

答案 1 :(得分:68)

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

答案 2 :(得分:15)

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

}

答案 3 :(得分:15)

以前我已经完成了@ P5ycH0所说的(1x1图像被拉伸),但是在@Brooks之后我认为在我的自定义-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated实现中覆盖了UITableViewCell并在调用{{1后重置了背景颜色选择/突出显示单元格时保留背景颜色

[super setHighlighted:highlighted animated:animated];

答案 4 :(得分:4)

Brooks对于为什么会发生这种情况有一个很好的解释,但我认为我有更好的解决方案。

在子视图中,将setBackgroundColor:覆盖为您想要的颜色。仍将调用setter,但仅强制指定您指定的颜色。

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

答案 5 :(得分:3)

好的,丢失UIView类的背景颜色是它在选定的tableviewcell中的正常行为。 我无法弄清楚如何防止这种情况。 现在我刚刚用一个包含拉伸的1x1白色像素的UIImageView替换了UIView。 丑陋的imo,但它确实有效。

答案 6 :(得分:3)

(最终)可以在iOS 13中解决此问题。在iOS 13 beta 3发行说明中找到了这一甜蜜的段落。

  

当单元格突出显示或选中时,UITableViewCell类不再更改contentView及其任何子视图的backgroundColor或isOpaque属性。如果要在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

答案 7 :(得分:3)

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

- (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是iOS 7中出现的UITableViewCell的属性。请注意,您可以使用自己的单元格的子视图或视图而不是它。

答案 8 :(得分:1)

将此添加到您的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 :(得分:1)

与@ Brooks的回答相关,这就是我在Swift和iOS8 / iOS9中使用它所做的。

  • 覆盖setSelectedsetHighlighted
  • 不要打电话给超级
  • 清除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 :(得分:1)

<强>摘要

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

基于mientus&#39; answer,我创建了一个解决方案,允许您指定哪些视图应保留其背景颜色

这仍然允许其他单元格子视图在突出显示/选择时删除其背景,并且是我们案例中唯一有效的解决方案(两个视图需要永久背景)。

我使用了一种面向协议的方法,"there is no package called 'dbplyr'" 协议包含要锁定的视图列表,并在保持颜色的同时运行闭包:

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

答案 11 :(得分: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
}

答案 12 :(得分:0)

在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:中取消选择它。

答案 13 :(得分:0)

在这个奇怪的问题上花了一些时间。我不想设置UITableViewCellSelectionStyleNone样式以在选择我的行时保留漂亮的动画。但是没有一个建议的想法对我有用 - 我试图覆盖setSelected和setHighlighted并在那里设置我的子视图backgroundColor - 它正在通过iOS保持重置并仍然闪烁(新颜色 - >旧颜色)。 对我来说,修复非常简单。 当我的行被选中时,另一个视图控制器被按下,用户在该屏幕上选择一些选项并调用委托,我根据用户选择更改颜色。在这个代表中,我只为我的单元格做了[cell setSelected:NO animated:NO]。 (我有静态UITableViewController并有单元格的出口)。 您可以在didSelect方法中取消选择单元格,但在我的情况下,我正在使用segues。

答案 14 :(得分:0)

您可以通过覆盖UITableViewCell类中的setHighlighted函数来更改tableViewCell的行为(您需要从中继承)。我更改单元格背景图像的代码示例:

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

您还可以在界面构建器中将选择模式更改为灰色,蓝色或无。

答案 15 :(得分:0)

这是我的看法。我有一个子类,我的所有单元都继承自,所以我这样做是为了避免我的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的问题。

答案 16 :(得分:0)

从那里你说你使用IB构建了一个tableViewCell,我想检查你是否将你的视图添加为UITableViewCell contentView的子视图,而不是view。内容视图是单元格显示的内容的默认超级视图。

参考文献:

  

UITableViewCell对象的内容视图是单元格显示的内容的默认超级视图。如果您想通过简单地添加其他视图来自定义单元格,则应将它们添加到内容视图中,以便在单元格进入和退出编辑模式时将它们正确定位。