【问题标题】:UIView backgroundColor disappears when UITableViewCell is selected选择 UITableViewCell 时 UIView backgroundColor 消失
【发布时间】:2011-07-10 12:19:21
【问题描述】:

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

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

如何让我的 UIView backgroundColor 在 tableView 被选中时显示?

【问题讨论】:

  • 视图中还有哪些其他界面元素会被单元格选择颜色覆盖?
  • 目前只是一张图片。它的目的是使图像看起来像一张照片。稍后,我也会在上面添加一个标签。

标签: ios uitableview uiview interface-builder


【解决方案1】:

这里的问题是 [super] 实现

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

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

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

以下是触摸单元格所产生的函数调用序列

  1. 设置高亮
  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 将所有背景颜色设置为 [r:0 b:0 g:0 a:0] 之前调用了 setHighlighted。

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

【讨论】:

  • 反响很好。很有道理!。我会继续关注覆盖 ;-)
  • 只是跟进。我已经搞砸了很多,但仍然没有找到我所说的用于覆盖 setSelected 的干净、通用的解决方案。如果/当我这样做时,我会发布更多信息......快乐编码
  • 如果你在 setSelected 中设置 UIView 背景颜色,你也应该设置它 setHighlighted 否则你可能会看到一些闪烁。
  • 至少在iOS 7上,还可以将单元格的selectionStyle设置为UITableViewCellSelectionStyleNone,并且不会清除背景。唯一的缺点是它会忽略selectedBackgroundView 属性。
  • 还有什么吗?已经 8 年了,我还在调整中
【解决方案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;
}

【讨论】:

  • 很好的解决方案。比所有子类和 CALayer 解决方案都要干净。
  • 谢谢!您的解决方案是最好的!
  • 这对我来说是最有用的答案。 Modified 稍微适合我的用例(具有多个锁定背景视图的多个单元格),但对工作解决方案表示敬意!
  • 有史以来最好的解决方案!
【解决方案3】:

当你的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
    }

}

【讨论】:

  • 快速的最佳答案
【解决方案4】:

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

【讨论】:

  • 感谢搜索了2天
  • 你节省了我的时间,非常感谢
  • 我认为创建这个setHighlighted 函数是为了在选择单元格时添加自定义行为,顺便说一句干得好
【解决方案5】:

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

当单元格被突出显示或被选中时,UITableViewCell 类不再更改 contentView 及其任何子视图的 backgroundColor 或 isOpaque 属性。如果您在 contentView 内(包括)单元格的任何子视图上设置不透明的背景颜色,则单元格突出显示或选中时的外观可能会受到影响。解决子视图问题的最简单方法是确保它们的 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 过时:( 因为它可能会保持隐藏状态
【解决方案6】:

布鲁克斯对为什么会发生这种情况有很好的解释,但我认为我有更好的解决方案。

在您的子视图中,将setBackgroundColor: 覆盖为您想要的任何颜色。仍然会调用 setter,但只会强制执行您指定的颜色。

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

【讨论】:

    【解决方案7】:

    好的,当 UIView 类位于选定的 tableviewcell 中时,失去其背景颜色是正常的行为。 我不知道如何防止这种情况。 现在,我刚刚将 UIView 替换为包含拉伸的 1x1 白色像素的 UIImageView。 丑陋的imo,但它有效。

    【讨论】:

      【解决方案8】:

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

      - (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 的属性。请注意,您可以使用自己的单元格的一个或多个子视图来代替它。

      【讨论】:

        【解决方案9】:

        将此添加到您的 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()
                })
            }
        }
        

        【讨论】:

          【解决方案10】:

          与@Brooks 的回答有关,这就是我为使其在 Swift 和 iOS8/iOS9 中工作所做的工作。

          • 覆盖setSelectedsetHighlighted
          • 不要叫 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()
                }
            }
            

          【讨论】:

            【解决方案11】:

            总结

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


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

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

            我使用了面向协议的方法,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]
                }
            }
            

            【讨论】:

              【解决方案12】:

              斯威夫特 4

              在您的 UITableViewCell 类中:

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

              【讨论】:

                【解决方案13】:

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

                来自参考:

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

                【讨论】:

                • 我在 InterfaceBuilders UITableViewCell 中的视图层次结构是 TableViewCell>View>ImageView。如果我使用代码构建 tableViewCell,我可能会错误地将我的视图添加到 tableviewcell 的视图而不是 contentView,但我不知道我怎么会在 IB 中犯这个错误......请注意,我的图像正在显示,但是父视图元素不是。
                【解决方案14】:

                您可以通过覆盖 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 而不是视图找到了解决方法。
                • 还有更多信息:在我最近的测试中,我展示了每次选择一个单元格时背景颜色都会被清除,而不仅仅是在初始化或你有什么之后。事件的顺序似乎是 1) setHighlighted 调用 2)touchesEnded 调用 3) layoutSubviews 调用 4) didSelectRowAtIndexPath 调用 4) layoutSubviews 再次调用。背景颜色似乎在 didSelectRowAtIndexPath 之前被清除
                【解决方案15】:

                在 iOS 7 中,对我有用的是在 UITableViewCell 子类中覆盖 setSelected:animated:,但与 @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: 中取消选择它。

                【讨论】:

                  【解决方案16】:

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

                  【讨论】:

                    【解决方案17】:

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-10-08
                      • 2011-10-26
                      • 1970-01-01
                      相关资源
                      最近更新 更多