为什么 UITableViewCell 选择时所有背景都会消失?

发布于 2024-11-29 14:03:25 字数 724 浏览 2 评论 0原文

我当前项目的 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];

尽管这会产生正确的背景外观,当我选择单元格时,会发生奇怪的副作用;所有其他背景都以某种方式关闭。下面的屏幕截图看起来应该是,但没有选择顶部单元格。显示黑色和灰色矩形区域,但它们消失了

模拟器的屏幕截图。顶部单元格被选中,底部单元格未被选中! .

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

My current project's UITableViewCell behavior is baffling me. I have a fairly straightforward subclass of UITableViewCell. It adds a few extra elements to the base view (via [self.contentView addSubview:...] and sets background colors on the elements to have them look like black and grey rectangular boxes.

Because the background of the entire table has this concrete-like texture image, each cell's background needs to be transparent, even when selected, but in that case it should darken a bit. I've set a custom semi-transparent selected background to achieve this effect:

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

[self setSelectedBackgroundView:background];

And although that yields the right look for the background, a weird side effect happens when I select the cell; all other backgrounds are somehow turnt off. Here's a screenshot. The bottom cell looks like it should and is not selected. The top cell is selected, but it should display the black and grey rectangular areas, yet they are gone!

Screenshot of the simulator. The top cell is selected, the bottom is not.

Who knows what's going on here and even more important: how can I correct this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

旧瑾黎汐 2024-12-06 14:03:25

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

例如,如果这些 UILabels 作为子视图添加到您的自定义单元格中,那么您可以将其添加到 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]];
}

What is happening is that each subview inside the TableViewCell will receive the setSelected and setHighlighted methods. The setSelected method will remove background colors but if you set it for the selected state it will be corrected.

For example if those are UILabels added as subviews in your customized cell, then you can add this to the setSelected method of your TableViewCell implementation code:

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

    self.textLabel.backgroundColor = [UIColor blackColor];

}

where self.textLabel would be whatever those labels are that are shown in the picture above

I'm not sure where your adding your selected view, I usually add it in the setSelected method.

Alternatively, you can subclass the UILabel and override the setHighlighted method like so:

-(void)setHighlighted:(BOOL)highlighted
{
    [self setBackgroundColor:[UIColor blackColor]];
}
鲜血染红嫁衣 2024-12-06 14:03:25

如果您不知道发生了什么,那么单元格突出显示过程可能会显得复杂且令人困惑。我彻底困惑了,并做了一些广泛的实验。以下是我的发现的注释,可能会对某人有所帮助(如果有人有任何要添加或反驳的内容,请发表评论,我会尽力确认和更新)

在正常的“未选择”状态

  • 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: 中):

A 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];

The cell highlighting process can seem complex and confusing if you don't know whats going on. I was thoroughly confused and did some extensive experimentation. Here's the notes on my findings that may help somebody (if anyone has anything to add to this or refute then please comment and I will endeavour to confirm and update)

In the normal “not selected” state

  • The contentView (whats in your XIB unless you coded it otherwise) is drawn normally
  • The selectedBackgroundView is HIDDEN
  • The backgroundView is visible (so provided your contentView is transparent you see the backgroundView or (if you have not defined a backgroundView you'll see the background colour of the UITableView itself)

A cell is selected, the following occurs immediately with-OUT any animation:

  • All views/subviews within the contentView have their backgroundColor cleared (or set to transparent), label etc text color's change to their selected colour
  • The selectedBackgroundView becomes visible (this view is always the full size of the cell (a custom frame is ignored, use a subview if you need to). Also note the backgroundColor of subViews are not displayed for some reason, perhaps they're set transparent like the contentView). If you didn't define a selectedBackgroundView then Cocoa will create/insert the blue (or gray) gradient background and display this for you)
  • The backgroundView is unchanged

When the cell is deselected, an animation to remove the highlighting starts:

  • The selectedBackgroundView alpha property is animated from 1.0 (fully opaque) to 0.0 (fully transparent).
  • The backgroundView is again unchanged (so the animation looks like a crossfade between selectedBackgroundView and backgroundView)
  • ONLY ONCE the animation has finished does the contentView get redrawn in the "not-selected" state and its subview backgroundColor's become visible again (this can cause your animation to look horrible so it is advisable that you don't use UIView.backgroundColor in your contentView)

CONCLUSIONS:

If you need a backgroundColor to persist through out the highlight animation, don't use the backgroundColor property of UIView instead you can try (probably with-in tableview:cellForRowAtIndexPath:):

A CALayer with a background color:

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

or a 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];

I've also used a CALayer.border technique to provide a custom UITableView seperator:

// 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];
一袭白衣梦中忆 2024-12-06 14:03:25

当您开始拖动 UITableViewCell 时,它会在其子视图上使用 0-alpha 颜色调用 setBackgroundColor:。我通过子类化 UIView 并重写 setBackgroundColor: 来忽略 0-alpha 颜色的请求来解决这个问题。它感觉很hacky,但它比任何其他 解决方案我已经遇到。

@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 的实例。

When you start dragging a UITableViewCell, it calls setBackgroundColor: on its subviews with a 0-alpha color. I worked around this by subclassing UIView and overriding setBackgroundColor: to ignore requests with 0-alpha colors. It feels hacky, but it's cleaner than any of the other solutions I've come across.

@implementation NonDisappearingView

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

@end

Then, I add a NonDisappearingView to my cell and add other subviews to it:

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

Alternatively, you could make cell.contentView an instance of NonDisappearingView.

前事休说 2024-12-06 14:03:25

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

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

您还需要使用 -setHighlighted:animated: 执行相同的操作。

My solution is saving the backgroundColor and restoring it after the super call.

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

You also need to do the same thing with -setHighlighted:animated:.

帅气尐潴 2024-12-06 14:03:25

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

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

Obj-C版本类似,这里主要是想法

Found a pretty elegant solution instead of messing with the tableView methods. You can create a subclass of UIView that ignores setting its background color to clear color. Code:

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

Obj-C version would be similar, the main thing here is the idea

浊酒尽余欢 2024-12-06 14:03:25

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

您可以在 GitHub 上找到 KeepBackgroundCell

通过 CocoaPods 将以下行添加到 Podfile 中来安装它:

pod 'KeepBackgroundCell'

用法:

< Swift

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

I created a UITableViewCell category/extension that allows you to turn on and off this transparency "feature".

You can find KeepBackgroundCell on GitHub

Install it via CocoaPods by adding the following line to your Podfile:

pod 'KeepBackgroundCell'

Usage:

Swift

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
寻找我们的幸福 2024-12-06 14:03:25

阅读完所有现有答案后,通过仅子类化 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)
        }
    }
}

Having read through all the existing answers, came up with an elegant solution using Swift by only subclassing UITableViewCell.

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)
        }
    }
}
2024-12-06 14:03:25

我们需要做的就是重写 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;

输入图片此处描述

All we need is to override the setSelected method and change the selectedBackgroundView for the tableViewCell in the custom tableViewCell class.

We need to add the backgroundview for the tableViewCell in cellForRowAtIndexPath method.

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

Next I have overridden the setSelected method as mentioned below.

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

}

Also one of the most important point to be noted is to change the tableViewCell selection style. It should not be UITableViewCellSelectionStyleNone.

lTableViewCell.selectionStyle = UITableViewCellSelectionStyleGray;

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文