【问题标题】:Why do all backgrounds disappear on UITableViewCell select?为什么 UITableViewCell 选择时所有背景都消失了?
【发布时间】:2011-10-26 13:40:42
【问题描述】:

我当前项目的 UITableViewCell 行为让我感到困惑。我有一个相当简单的 UITableViewCell 子类。它向基本视图添加了一些额外的元素(通过[self.contentView addSubview:...] 并在元素上设置背景颜色,使它们看起来像黑色和灰色的矩形框。

因为整个表格的背景都有这种混凝土般的纹理图像,所以每个单元格的背景都需要是透明的,即使在被选中时也是如此,但在这种情况下,它应该变暗一点。我已经设置了一个自定义的半透明选定背景来实现这个效果:

UIView *background = [[[UIView alloc] initWithFrame:self.bounds] autorelease];
background.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
background.opaque = NO;

[self setSelectedBackgroundView:background];

虽然这会产生正确的背景外观,但当我选择单元格时会发生奇怪的副作用;所有其他背景都以某种方式关闭。这是一个屏幕截图。底部单元格看起来应该并且未被选中。顶部的单元格被选中,但它应该显示黑色和灰色的矩形区域,但它们已经消失了!

谁知道这里发生了什么,更重要的是:我该如何纠正这个问题?

【问题讨论】:

  • 我知道一种解决方案是摆脱所有子视图并在 drawRect: 方法中“手动”绘制所有内容,但这不是一个选项,因为类现在依赖于这种机制。
  • 好建议,@ms83,但最终结果完全一样,只是现在背景上有一张图片。仍然所有其他背景“消失”。并不是它们也被设置为那个背景,因为这样多个透明视图应该堆叠起来并且仍然可见,即使是以非预期的方式。
  • 这听起来像是一个缓存问题。也许创建两个 CellIdentifiers,一个用于选定的单元格,另一个用于未选定的单元格。根据需要初始化任一类型的单元格和出队。
  • 嗨@Wolfgang,我不太明白你的意思。您在图像中看到的只是同一个单元格的选定状态和常规状态。选择是由于我在截屏时按住鼠标按钮(模拟器)。同样的事情发生在设备上。

标签: objective-c cocoa-touch uitableview subclassing


【解决方案1】:

发生的情况是 TableViewCell 中的每个子视图都将收到 setSelected 和 setHighlighted 方法。 setSelected 方法将删除背景颜色,但如果您将其设置为选定状态,它将被纠正。

例如,如果这些是作为子视图添加到自定义单元格中的 UILabel,那么您可以将其添加到 TableViewCell 实现代码的 setSelected 方法中:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    self.textLabel.backgroundColor = [UIColor blackColor];

}

self.textLabel 是上图中显示的标签

我不确定你在哪里添加你选择的视图,我通常在 setSelected 方法中添加它。

或者,您可以继承 UILabel 并覆盖 setHighlighted 方法,如下所示:

-(void)setHighlighted:(BOOL)highlighted
{
    [self setBackgroundColor:[UIColor blackColor]];
}

【讨论】:

  • 您好,感谢您回答这个问题。一定是给了你某种徽章,因为它已经有一段时间没有得到答复了。谢谢!
  • 不要忘记在第二种方法中调用 super setHighlighted ;)
  • 嗯,setSelected 似乎在 iOS 8 中不起作用,但 setHighlighted 覆盖有效,谢谢!
  • 为什么选择苹果?为什么?
【解决方案2】:

如果您不知道发生了什么,单元格突出显示过程可能看起来很复杂且令人困惑。我彻底糊涂了,做了一些广泛的实验。这是我的发现可能对某人有所帮助的注释(如果有人对此有任何补充或反驳,请发表评论,我将努力确认和更新)

处于正常的“未选中”状态

  • contentView(XIB 中的内容,除非您另外编码)正常绘制
  • selectedBackgroundView 已隐藏
  • backgroundView 是可见的(因此,如果您的 contentView 是透明的,您会看到 backgroundView 或(如果您尚未定义 backgroundView,您将看到 UITableView 本身的背景颜色)

选择了一个单元格,以下内容立即发生,没有任何动画:

  • contentView 中的所有视图/子视图都将其 backgroundColor 清除(或设置为透明)、标签等文本颜色更改为所选颜色
  • selectedBackgroundView 变为可见(此视图始终是单元格的完整大小(忽略自定义框架,如果需要,请使用子视图)。还要注意 subViewsbackgroundColor 不显示出于某种原因,也许它们像contentView 一样被设置为透明的)。如果您没有定义selectedBackgroundView,那么 Cocoa 将创建/插入蓝色(或灰色)渐变背景并为您显示)
  • backgroundView 不变

当取消选择单元格时,会开始动画移除突出显示:

  • selectedBackgroundView alpha 属性从 1.0(完全不透明)动画到 0.0(完全透明)。
  • backgroundView 再次保持不变(因此动画看起来像是 selectedBackgroundViewbackgroundView 之间的交叉淡入淡出)
  • 只有在动画完成后,contentView 才会在“未选择”状态下重绘,并且其子视图 backgroundColor 会再次可见(这可能会导致您的动画看起来很糟糕,因此建议您不要在contentView 中使用UIView.backgroundColor

结论:

如果您需要一个backgroundColor 来坚持整个高亮动画,请不要使用UIViewbackgroundColor 属性,您可以尝试(可能与tableview:cellForRowAtIndexPath: 一起使用):

具有背景颜色的 CALayer:

UIColor *bgColor = [UIColor greenColor];
CALayer* layer = [CALayer layer];
layer.frame = viewThatRequiresBGColor.bounds;
layer.backgroundColor = bgColor.CGColor;
[cell.viewThatRequiresBGColor.layer addSublayer:layer];

或 CAGradientLayer:

UIColor *startColor = [UIColor redColor];
UIColor *endColor = [UIColor purpleColor];
CAGradientLayer* gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = viewThatRequiresBGColor.bounds;
gradientLayer.colors = @[(id)startColor.CGColor, (id)endColor.CGColor];
gradientLayer.locations = @[[NSNumber numberWithFloat:0],[NSNumber numberWithFloat:1]];
[cell.viewThatRequiresBGColor.layer addSublayer:gradientLayer];

我还使用了 CALayer.border 技术来提供自定义 UITableView 分隔符:

// We have to use the borderColor/Width as opposed to just setting the 
// backgroundColor else the view becomes transparent and disappears during 
// the cell's selected/highlighted animation
UIView *separatorView = [[UIView alloc] initWithFrame:CGRectMake(0, 43, 1024, 1)];
separatorView.layer.borderColor = [UIColor redColor].CGColor;
separatorView.layer.borderWidth = 1.0;
[cell.contentView addSubview:separatorView];

【讨论】:

  • Mitical :-) …但是仅仅对视图层采取行动而不是创建一个新层还不够吗?就像 viewThatRequiresBGColor.layer = bgColor.CGColor;
  • 值得一提的是,backgroundView 仅适用于 UITableViewCellUITableViewStyleGrouped 样式。
  • 非常感谢。我有一个类似的问题。这帮助我解决了这个问题。
【解决方案3】:

当您开始拖动 UITableViewCell 时,它会在其子视图上以 0-alpha 颜色调用 setBackgroundColor:。我通过子类化 UIView 并覆盖 setBackgroundColor: 来解决这个问题,以忽略具有 0-alpha 颜色的请求。感觉很hacky,但比我遇到的othersolutions中的any干净。

@implementation NonDisappearingView

-(void)setBackgroundColor:(UIColor *)backgroundColor {
    CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
    if (alpha != 0) {
        [super setBackgroundColor:backgroundColor];
    }
}

@end

然后,我将NonDisappearingView 添加到我的单元格并添加其他子视图:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellIdentifier = @"cell";    
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
        UIView *background = [cell viewWithTag:backgroundTag];
        if (background == nil) {
            background = [[NonDisappearingView alloc] initWithFrame:backgroundFrame];
            background.tag = backgroundTag;
            background.backgroundColor = backgroundColor;
            [cell addSubview:background];
        }

        // add other views as subviews of background
        ...
    }
    return cell;
}

或者,您可以将 cell.contentView 设为 NonDisappearingView 的实例。

【讨论】:

  • 感谢您提供有趣的解决方案。我不明白为什么 Apple 决定实现这种子视图背景颜色行为。干杯!
  • 像魅力一样工作!谢谢!!
【解决方案4】:

我的解决方案是保存backgroundColor 并在超级调用后恢复它。

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    UIColor *bgColor = self.textLabel.backgroundColor;
    [super setSelected:selected animated:animated];
    self.textLabel.backgroundColor = bgColor;
}

你也需要对-setHighlighted:animated:做同样的事情。

【讨论】:

  • 嘿——这就是我在查看其他答案后所做的。似乎运作良好。
【解决方案5】:

找到了一个非常优雅的解决方案,而不是搞乱 tableView 方法。您可以创建 UIView 的子类,忽略将其背景颜色设置为清除颜色。代码:

class NeverClearView: UIView {
    override var backgroundColor: UIColor? {
        didSet {
            if UIColor.clearColor().isEqual(backgroundColor) {
                backgroundColor = oldValue
            }
        }
    }
}

Obj-C 版本也差不多,主要是思路

【讨论】:

    【解决方案6】:

    我创建了一个 UITableViewCell 类别/扩展,允许您打开和关闭此透明度“功能”。

    你可以找到 KeepBackgroundCell on GitHub

    通过 CocoaPods 将其添加到您的 Podfile 中:

    pod 'KeepBackgroundCell'
    

    用法:

    斯威夫特

    let cell = <Initialize Cell>
    cell.keepSubviewBackground = true  // Turn  transparency "feature" off
    cell.keepSubviewBackground = false // Leave transparency "feature" on
    

    Objective-C

    UITableViewCell* cell = <Initialize Cell>
    cell.keepSubviewBackground = YES;  // Turn  transparency "feature" off
    cell.keepSubviewBackground = NO;   // Leave transparency "feature" on
    

    【讨论】:

      【解决方案7】:

      阅读所有现有答案后,通过仅继承 UITableViewCell 的方式使用 Swift 提出了一个优雅的解决方案。

      extension UIView {
          func iterateSubViews(block: ((view: UIView) -> Void)) {
              for subview in self.subviews {
                  block(view: subview)
                  subview.iterateSubViews(block)
              }
          }
      }
      
      class CustomTableViewCell: UITableViewCell {
         var keepSubViewsBackgroundColorOnSelection = false
      
          override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
              super.init(style: style, reuseIdentifier: reuseIdentifier)
          }
      
          // MARK: Overrides
          override func setSelected(selected: Bool, animated: Bool) {
              if self.keepSubViewsBackgroundColorOnSelection {
                  var bgColors = [UIView: UIColor]()
                  self.contentView.iterateSubViews() { (view) in
      
                      guard let bgColor = view.backgroundColor else {
                          return
                      }
      
                      bgColors[view] = bgColor
                  }
      
                  super.setSelected(selected, animated: animated)
      
                  for (view, backgroundColor) in bgColors {
                      view.backgroundColor = backgroundColor
                  }
              } else {
                  super.setSelected(selected, animated: animated)
              }
          }
      
          override func setHighlighted(highlighted: Bool, animated: Bool) {
              if self.keepSubViewsBackgroundColorOnSelection {
                  var bgColors = [UIView: UIColor]()
                  self.contentView.iterateSubViews() { (view) in
                      guard let bgColor = view.backgroundColor else {
                          return
                      }
      
                      bgColors[view] = bgColor
                  }
      
                  super.setHighlighted(highlighted, animated: animated)
      
                  for (view, backgroundColor) in bgColors {
                      view.backgroundColor = backgroundColor
                  }
              } else {
                  super.setHighlighted(highlighted, animated: animated)
              }
          }
      }
      

      【讨论】:

        【解决方案8】:

        我们只需要重写 setSelected 方法并更改自定义 tableViewCell 类中 tableViewCell 的 selectedBackgroundView。

        我们需要在 cellForRowAtIndexPath 方法中为 tableViewCell 添加背景视图。

        lCell.selectedBackgroundView = [[UIView alloc] init];
        

        接下来我重写了 setSelected 方法,如下所述。

        - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
        [super setSelected:selected animated:animated];
        
        // Configure the view for the selected state
        
        UIImageView *lBalloonView = [self viewWithTag:102];
        [lBalloonView setBackgroundColor:[[UIColor hs_globalTint] colorWithAlphaComponent:0.2]];
        
        UITextView *lMessageTextView = [self viewWithTag:103];
        lMessageTextView.backgroundColor    = [UIColor clearColor];
        
        UILabel *lTimeLabel = [self viewWithTag:104];
        lTimeLabel.backgroundColor  = [UIColor clearColor];
        
        }
        

        另外需要注意的最重要的一点是改变tableViewCell的选择风格。它不应该是 UITableViewCellSelectionStyleNone。

        lTableViewCell.selectionStyle = UITableViewCellSelectionStyleGray;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-05-24
          • 1970-01-01
          • 2011-07-10
          • 2010-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多