【问题标题】:UITableViewCell subview disappears when cell is selected选择单元格时 UITableViewCell 子视图消失
【发布时间】:2011-10-08 10:09:55
【问题描述】:

我正在实现一个颜色选择器表视图,用户可以在其中选择 10 种颜色(取决于产品)。用户还可以选择其他选项(如硬盘容量,...)。

所有颜色选项都在它们自己的 tableview 部分中。

我想在 textLabel 左侧显示一个小方块,显示实际颜色。

现在我正在添加一个简单的方形 UIView,给它正确的背景颜色,如下所示:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RMProductAttributesCellID];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:RMProductAttributesCellID] autorelease];
        cell.indentationWidth = 44 - 8;

        UIView *colorThumb = [[[UIView alloc] initWithFrame:CGRectMake(8, 8, 28, 28)] autorelease];
        colorThumb.tag = RMProductAttributesCellColorThumbTag;
        colorThumb.hidden = YES;
        [cell.contentView addSubview:colorThumb];
    }

    RMProductAttribute *attr = (RMProductAttribute *)[_product.attributes objectAtIndex:indexPath.section];
    RMProductAttributeValue *value = (RMProductAttributeValue *)[attr.values objectAtIndex:indexPath.row];
    cell.textLabel.text = value.name;
    cell.textLabel.backgroundColor = [UIColor clearColor];

    UIView *colorThumb = [cell viewWithTag:RMProductAttributesCellColorThumbTag];
    colorThumb.hidden = !attr.isColor;
    cell.indentationLevel = (attr.isColor ? 1 : 0);

    if (attr.isColor) {
        colorThumb.layer.cornerRadius = 6.0;
        colorThumb.backgroundColor = value.color;
    }

    [self updateCell:cell atIndexPath:indexPath];

    return cell;
}

这显示没有问题。

我唯一的问题是,当我选择“颜色”行时,在淡入蓝色选择动画期间,我的自定义 UIView (colorThumb) 被隐藏。它在选择/取消选择动画结束后再次可见,但这会产生丑陋的伪影。

我应该怎么做才能纠正这个问题?我不是在正确的位置插入子视图吗?

(didSelectRowAtIndexPath没有什么特别的,我只是把cell的附件改成checkbox或者什么都没有,把当前的indexPath取消选中)。

【问题讨论】:

  • upadteCell 是什么?
  • updateCell 做了一些小的调整,比如是否设置复选标记,根据可用性选择文本颜色,......但与单元格本身或 colorThumb 没有真正的变化。
  • 接受的答案没有提供解决方案,请参阅下面我的答案以获取解决方案

标签: iphone ios uitableview


【解决方案1】:

UITableViewCell在单元格被选中或高亮时改变所有子视图的背景颜色,你可以通过覆盖Tableview单元格的setSelected:animatedsetHighlighted:animated并重置视图背景颜色来解决这个问题。

在目标 C 中:

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

    if (selected){
        self.yourView.backgroundColor = color;
    }
}

-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated{
    UIColor *color = self.yourView.backgroundColor;        
    [super setHighlighted:highlighted animated:animated];

    if (highlighted){
        self.yourView.backgroundColor = color;
    }
}

在 Swift 3.1 中:

override func setSelected(_ selected: Bool, animated: Bool) {
    let color = yourView.backgroundColor         
    super.setSelected(selected, animated: animated)

    if selected {
        yourView.backgroundColor = color
    }
}

override func setHighlighted(_ highlighted: Bool, animated: Bool) {
    let color = yourView.backgroundColor
    super.setHighlighted(highlighted, animated: animated)

    if highlighted {
        yourView.backgroundColor = color
    }
}

【讨论】:

  • 我们需要if (highlighted)if (selected) 条件吗?如果我们没有这些条件,我认为它会起作用。
  • @RishabhTayal 基本上是为了避免用相同的值覆盖变量
  • 请注意,当您在选择项目时更改背景颜色时,当项目变为未选择时,旧的(错误的)颜色可能会恢复。如果可能发生这种情况,请删除 if(突出显示)和 if(选定)条件。
  • 这种做法取消了动画,所以没有任何意义。最好将单元格选择样式设置为 .none。
【解决方案2】:

这是因为表格视图单元格会自动更改内容视图内所有视图的背景颜色以突出显示状态。您可以考虑将UIView 子类化以绘制您的颜色或使用UIImageView 与自定义1x1 px 拉伸图像。

【讨论】:

  • 愚弄我。当然就是这样,子视图必须是透明的,这样选择动画才能正确发生。谢谢!
  • 或者你可以重新设置背景颜色覆盖setHighlighted:animated:setSelected:animated:
  • 不知何故重置背景颜色对我不起作用(在 iOS 8.1 上运行)。而是通过将我的视图子类化并将 setBackgroundColor 覆盖为 [super setBackgroundColor:[UIColor whiteColor]] 来解决此问题。
【解决方案3】:

找到了一个非常优雅的解决方案,而不是搞乱 tableViewCell 选择/突出显示方法。您可以创建一个 UIView 的子类,忽略将其背景颜色设置为清除颜色。

斯威夫特 3/4:

class NeverClearView: UIView {
    override var backgroundColor: UIColor? {
        didSet {
            if backgroundColor != nil && backgroundColor!.cgColor.alpha == 0 {
                backgroundColor = oldValue
            }
        }
    }
}

斯威夫特 2:

class NeverClearView: UIView {
    override var backgroundColor: UIColor? {
        didSet {
            if CGColorGetAlpha(backgroundColor!.CGColor) != 0 {
                backgroundColor = oldValue
            }
        }
    }
}

Obj-C 版本:

@interface NeverClearView : UIView

@end

@implementation NeverClearView

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

@end

【讨论】:

  • 这很可爱。如果您有一些可重复使用的视图(如“徽章”或“标签”),那么这很容易成为最佳解决方案,而这些视图应该永远不会有清晰的背景。 ::rant :: 多么令人困惑的解决方案@UIKit,在您进行单元格选择时将所有子视图设置为透明。至少限制为单元格的完整高度或宽度的子视图,或者 N 深度的子视图。
  • @SimplGy 突出显示的深度确实是一个不错的选择,但是嘿 - 这是 UIKit,我见过比这更糟糕的事情 =)
  • 在我将 if 更改为 CGColorGetAlpha(backgroundColor!.CGColor) == 0 后为我工作,因为它不等于 clearColor
  • 很好的解决方案! iOS 13 SDK 不再需要。
【解决方案4】:

对于 Swift 2.2 这是可行的

cell.selectionStyle = UITableViewCellSelectionStyle.None

原因由@Andriy解释

这是因为表格视图单元格会自动更改背景颜色 突出显示状态的内容视图内的所有视图。

【讨论】:

    【解决方案5】:

    解决问题的另一种方法是用核心图形渐变填充视图,例如:

    CAGradientLayer* gr = [CAGradientLayer layer];
    gr.frame = mySubview.frame;
    gr.colors = [NSArray arrayWithObjects:
                         (id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:.5] CGColor]
                         ,(id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:.5] CGColor]
                         , nil];
    
    gr.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0],[NSNumber numberWithFloat:1],nil];
    
    [mySubview.layer insertSublayer:gr atIndex:0];
    

    【讨论】:

    • 嗯,我正在尝试这个确切的代码,但它对我没有任何影响。我的子视图是作为 cell.contentView 的子视图添加的 UILabel,并在 iOS 6.0.1 下进行测试,以防万一。
    • 您将上面的代码应用于什么?您是否尝试过将标签简单地添加到单元格视图中?
    • 这是我认为的完美解决方案。绘制到图层完美地解决了这个问题,同时保持了完全的灵活性。我不喜欢使用 UIImageView 的解决方案,因为这样就更难调整渐变或颜色(每次都必须制作新图像),并且仅仅为此而对 UIView 进行子类化似乎有点过头了。
    • @Lyida,我不是真正的 Swift 开发者。 (我什至更 C#-one)。但据我所见,这不是特定于语言的事情,而是主要是 Cocoa/iOS 框架逻辑之一。因此,这个想法只是将几乎透明的 CAGradientLayer 放置到您的视图中以获得请求的结果。
    【解决方案6】:

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

    斯威夫特

    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
    

    KeepBackgroundCell 与 CocoaPods 兼容。你可以找到它on GitHub

    【讨论】:

      【解决方案7】:

      你可以cell.selectionStyle = UITableViewCellSelectionStyleNone;, 然后将 backgroundColor 设置为- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath

      【讨论】:

        【解决方案8】:

        Yatheesha B L 的回答启发。

        如果你调用 super.setSelected(selected, animated:animated),它会清除你设置的所有背景颜色。所以,我们不会调用超级方法。

        在 Swift 中:

        override func setSelected(selected: Bool, animated: Bool) {    
            if(selected)  {
                contentView.backgroundColor = UIColor.red 
            } else {
                contentView.backgroundColor = UIColor.white
            }
        }
        
        override func setHighlighted(highlighted: Bool, animated: Bool) {
            if(highlighted) {
                contentView.backgroundColor = UIColor.red 
            } else {
                contentView.backgroundColor = UIColor.white
            }
        }
        

        【讨论】:

        • 感谢您的解决方案。 +1 覆盖 ishiglighted 变量和 sethighlighted 方法是不同的东西。正确的答案是重写方法。
        【解决方案9】:

        对于可能的情况,这是为了避免单元格中所有项目的灰色(如果您使用自定义表格视图单元格):

        1. 将 selectionStyle 设置为 .none ? selectionStyle = .none

        2. 重写此方法。

          func setHighlighted(_ highlight: Bool, animated: Bool)

        3. 调用超级,以获得超级设置的好处。

          super.setHighlighted(高亮,动画:动画)

        4. 做任何你想做的突出逻辑。

          override func setHighlighted(_ highlighted: Bool, animated: Bool) {
                super.setHighlighted(highlighted, animated: animated)
                // Your Highlighting Logic goes here...
          }
          

        【讨论】:

          【解决方案10】:

          UITableViewCell 出于某种原因在选择时更改所有子视图的背景颜色。

          这可能会有所帮助:

          DVColorLockView

          使用类似的方法来阻止 UITableView 在选择期间更改视图颜色。

          【讨论】:

            【解决方案11】:

            绘制视图而不是设置背景颜色

            import UIKit
            
            class CustomView: UIView {
            
                var fillColor:UIColor!
            
                convenience init(fillColor:UIColor!) {
                    self.init()
                    self.fillColor = fillColor
                }
            
                override func drawRect(rect: CGRect) {
                    if let fillColor = fillColor {
                        let context = UIGraphicsGetCurrentContext()
                        CGContextSetFillColorWithColor(context, fillColor.CGColor);
                        CGContextFillRect (context, self.bounds);
            
                    }
                }
            
            
            }
            

            【讨论】:

              【解决方案12】:

              最简单解决方案没有动画错误(如评价最高的答案)并且没有子类化和绘图 - 设置图层的边框颜色而不是 backgroundColor 并设置非常大的边框宽度。

              colorThumb.layer.cornerRadius = 6
              colorThumb.layer.borderWidth = colorThumb.frame.width
              colorThumb.layer.borderColor = value.color
              

              【讨论】:

                【解决方案13】:

                试试下面的代码:

                -(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
                {     
                [super setHighlighted:highlighted animated:animated];
                //Set your View's Color here.
                }
                

                【讨论】:

                  【解决方案14】:

                  不要忘记覆盖 setSelectedsetHighlighted

                  override func setHighlighted(highlighted: Bool, animated: Bool) {
                  
                      super.setHighlighted(highlighted, animated: animated)
                      someView.backgroundColor = .myColour()
                  }
                  
                  override func setSelected(selected: Bool, animated: Bool) {
                  
                      super.setSelected(selected, animated: animated)
                      someView.backgroundColor = .myColour()
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    这类似于 Pavel Gurov 的回答,但更灵活,因为它允许任何颜色是永久的。

                    class PermanentBackgroundColorView: UIView {
                        var permanentBackgroundColor: UIColor? {
                            didSet {
                                backgroundColor = permanentBackgroundColor
                            }
                        }
                    
                        override var backgroundColor: UIColor? {
                            didSet {
                                if backgroundColor != permanentBackgroundColor {
                                    backgroundColor = permanentBackgroundColor
                                }
                            }
                        }
                    }
                    

                    【讨论】:

                      【解决方案16】:

                      我想保留默认选择行为,除了一个我想忽略自动背景颜色更改的单元格子视图。但我还需要能够在其他时间更改背景颜色。

                      我想出的解决方案是子类化UIView,这样它就忽略了正常设置背景颜色,并添加了一个单独的函数来绕过保护。

                      斯威夫特 4

                      class MyLockableColorView: UIView {
                          func backgroundColorOverride(_ color: UIColor?) {
                                  super.backgroundColor = color
                          }
                      
                          override var backgroundColor: UIColor? {
                              set {
                                  return
                              }
                              get {
                                  return super.backgroundColor
                              }
                          }
                      }
                      

                      【讨论】:

                        【解决方案17】:

                        这是我的解决方案,使用 contentView 显示 selectionColor,效果很好

                        #import "BaseCell.h"
                        
                        @interface BaseCell ()
                        @property (nonatomic, strong) UIColor *color_normal;
                        @property (nonatomic, assign) BOOL needShowSelection;
                        @end
                        
                        
                        @implementation BaseCell
                        @synthesize color_customSelection;
                        @synthesize color_normal;
                        @synthesize needShowSelection;
                        
                        - (void)awakeFromNib
                        {
                            [super awakeFromNib];
                            [self setup];
                        }
                        
                        - (void)setup
                        {
                            //save normal contentView.backgroundColor
                            self.color_normal = self.backgroundColor;
                            if (self.color_normal == nil) {
                                self.color_normal = [UIColor colorWithRGBHex:0xfafafa];
                            }
                            self.color_customSelection = [UIColor colorWithRGBHex:0xF1F1F1];
                            self.accessoryView.backgroundColor = [UIColor clearColor];
                            if (self.selectionStyle == UITableViewCellSelectionStyleNone) {
                                needShowSelection = NO;
                            }
                            else {
                                //cancel the default selection
                                needShowSelection = YES;
                                self.selectionStyle = UITableViewCellSelectionStyleNone;
                            }
                        }
                        
                        /*
                         solution is here
                         */
                        - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
                        {
                            [super touchesBegan:touches withEvent:event];
                            if (needShowSelection) {
                                self.contentView.backgroundColor = self.backgroundColor = color_customSelection;
                            }
                        }
                        
                        - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
                        {
                            [super touchesCancelled:touches withEvent:event];
                            if (needShowSelection) {
                                self.contentView.backgroundColor = self.backgroundColor = color_normal;
                            }
                        }
                        
                        - (void)setSelected:(BOOL)selected animated:(BOOL)animated
                        {
                            [super setSelected:selected animated:animated];
                            if (needShowSelection) {
                                UIColor *color  = selected ? color_customSelection:color_normal;
                                self.contentView.backgroundColor = self.backgroundColor = color;
                            }
                        }
                        

                        【讨论】:

                          【解决方案18】:

                          将此代码放在UITableViewCell 的子类中

                          Swift 3 语法

                          override func setSelected(_ selected: Bool, animated: Bool) {
                              super.setSelected(selected, animated: animated)
                          
                              if(selected) {
                                  lockerSmall.backgroundColor = UIColor.init(red: 233/255, green: 106/255, blue: 49/255, alpha: 1.0)
                              }
                          }
                          
                          
                          override func setHighlighted(_ highlighted: Bool, animated: Bool) {
                              super.setHighlighted(highlighted, animated: animated)
                          
                              if(highlighted) {
                                  lockerSmall.backgroundColor = UIColor.init(red: 233/255, green: 106/255, blue: 49/255, alpha: 1.0)
                              }
                          }
                          

                          【讨论】:

                            【解决方案19】:

                            如果您使用情节提要,请添加另一个解决方案。创建一个 UIView 的子类,它不允许在初始设置后设置 backgroundColor

                            @interface ConstBackgroundColorView : UIView
                            
                            @end
                            
                            @implementation ConstBackgroundColorView
                            
                            - (void)setBackgroundColor:(UIColor *)backgroundColor {
                                if (nil == self.backgroundColor) {
                                    [super setBackgroundColor:backgroundColor];
                                }
                            }
                            
                            @end
                            

                            【讨论】:

                              【解决方案20】:

                              如果上面提到的后台解决方案不能解决您的问题,您的问题可能在于您的 tableView 的datasource

                              对我来说,我正在创建一个 DataSource 对象的实例(称为 BoxDataSource)来处理委托和 dataSource tableView 方法,如下所示:

                              //In cellForRowAtIndexPath, when setting up cell
                              let dataSource = BoxDataSource(delegate: self)
                              cell.tableView.dataSource = dataSource
                              return cell
                              

                              这导致每次点击单元格时都会释放数据源,因此所有内容都消失了。原因是 ARC 释放/垃圾收集性质。

                              为了解决这个问题,我必须进入自定义单元格,添加一个数据源变量:

                              //CustomCell.swift
                              var dataSource: BoxDataSource?
                              

                              然后,您需要将 dataSource 设置为您刚刚在 cellForRow 中创建的单元格的 dataSource var,因此不会使用 ARC 释放它。

                              cell.statusDataSource = BoxAssigneeStatusDataSource(delegate: self)
                              cell.detailsTableView.dataSource = cell.statusDataSource
                              return cell
                              

                              希望对您有所帮助。

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 2020-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2016-12-08
                                相关资源
                                最近更新 更多