选择UITableViewCell时,UIView backgroundColor消失


70

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

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

如何让我的UIView的backgroundColor时选择的tableView时,说明了什么?


视图内还有哪些其他界面元素被单元格选择颜色覆盖?
hennes 2011年

目前,这只是一张图片。旨在使图像看起来像照片。稍后,我还将在上面添加标签。
Tycho Pandelaar 2011年

Answers:


108

这里的问题是[super]实现

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

将UITableViewCell中的所有背景色设置为rgba(0,0,0,0)。为什么?也许让我们都汗流sweat背?

并不是整个视图都会消失(事实证明,如果您更改视图图层边框属性,这些属性将保留)

这是触摸单元格所产生的函数调用顺序

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

所以你的选择是

  1. 覆盖-(void)setSelected:(BOOL)选择动画:(BOOL)动画; 而不调用[super setSelected:selected animation:animated]。这将为您提供最技术上正确的实现,因为a)代码被包装在UITableViewCell子类内,并且b)因为仅在需要时才调用(需要时调用两次,但是有办法解决)。缺点是您必须重新实现setSelected的所有必需功能(与不必要的颜色清除功能相对)。现在,不要问我如何正确重写setSelected。您的猜测现在和我的一样好(请耐心等待,一旦找到答案,我将编辑此答案)。
  2. 重新在didSelectRowAtIndexPath中设置背景色。这不是很好,因为它会将应作为实例代码的内容放在实例之外。它的好处是,仅在需要时才调用它,而不是...
  3. layoutSubviews中重新声明背景色。这根本不是什么好事,因为layoutSubviews的调用时间像百万次!桌子每次刷新,每次滚动,祖母每次烫发都被称为……认真地说,是一百万次。这意味着有很多不必要的后台重新声明和很多额外的处理开销。从好的方面来说,它把代码放在UITableViewCell子类中,这很好。

不幸的是,在setHighlighted中重新声明背景色没有任何作用,因为在第一次调用setSelected之前将所有背景色设置为[r:0 b:0 g:0 a:0]之前会调用setHighlighted。

// TODO:给出有关如何覆盖setSelected(敬请期待)的详细说明


1
很好的回应。很有道理!我会继续关注;-)
第谷潘德拉尔

只是跟进。我对此颇为困惑,但仍然没有找到我所说的覆盖setSelected的干净通用解决方案。如果/何时我将发布更多信息。...祝您
Brooks

1
如果要在setSelected中设置UIView背景色,则还应该将其设置为setHighlighted,否则可能会出现闪烁。
nh32rg

10
至少在iOS 7上,您还可以将单元格的设置selectionStyleUITableViewCellSelectionStyleNone,并且不会清除背景。唯一的缺点是它将忽略该selectedBackgroundView属性。
奥斯汀

还有吗 已经八年了,我仍然很想念
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解决方案都干净得多。
乔尔2014年

谢谢!您的解决方案是最好的!
Insider 2016年

对我来说,这是最有用的答案。针对我的用例(具有多个具有锁定背景视图的多个单元格)进行了稍微修改,但是对于有效的解决方案来说是很赞的!
Yasir

有史以来最好的解决方案!
周永强

17

UITableViewCell选择您的状态时,应注意两种状态:HighlightedSelected

因此,对于您具有作为的子类的自定义单元格类的方案UITableViewCell,您可以轻松地覆盖这两种方法来避免这种情况(快速):

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
    }

}

快速的最佳答案
Rukshan Marapana

16

以前我已经按照@ 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;
}

谢谢您搜寻了2天
sanjeev 2014年

您节省了我的时间非常感谢
mychar

我认为这setHighlighted是创建函数选定单元格时添加自定义行为,顺便说一句好工作
温迪西甲

6

这个问题可以(最终)在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


最终,他们解决了这个奇怪的问题
第谷潘德拉尔

但是,这使得selectedBackgroundView过时:(因为它可能会留隐患
维勒姆·库尔兹

4

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

在子视图中,覆盖所需的setBackgroundColor:颜色。setter仍将被调用,但仅会强制您指定的颜色。

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

3

好的,在选定的tableviewcell中,失去UIView类的背景色是正常现象。我不知道如何防止这种情况。现在,我刚刚将UIView替换为包含拉伸的1x1白色像素的UIImageView。丑陋的imo,但它可以工作。


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是的财产UITableViewCell是出现在iOS的7需要注意的是,你可以用自己的细胞的子视图或观点,而不是它。


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

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

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

斯威夫特4

在您的UITableViewCell类中:

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

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

0

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

从参考:

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


我在InterfaceBuilders UITableViewCell中的视图层次是TableViewCell> View> ImageView。如果我使用代码构建tableViewCell,可能会错误地将视图添加到tableviewcell的视图中,而不是将contentView添加到tableViewcell的视图中,但是我看不到如何在IB中犯该错误。父视图元素不是。
Tycho Pandelaar 2011年

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

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


是的我知道。但这不是我的问题。我想知道是否可以防止基于UIView的元素在选定的tableviewcell上失去背景色。我通过使用imageview而不是视图找到了解决方法。
Tycho Pandelaar 2011年

还有更多信息:在我的最新测试中,我证明了每次选择单元格时都会清除背景色,而不仅仅是在初始化之后或您拥有了什么之后。事件的顺序似乎是1)setHighlighted称为2)touchesEnded称为3)layoutSubviews称为4)didSelectRowAtIndexPath称为4)layoutSubviews再次被调用。背景颜色似乎在didSelectRowAtIndexPath
Brooks

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:


0

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


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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.