【问题标题】:Where to highlight UICollectionViewCell: delegate or cell?在哪里突出显示 UICollectionViewCell:委托还是单元格?
【发布时间】:2013-03-18 17:35:39
【问题描述】:

根据Collection View Programming Guide 应该处理UICollectionViewDelegate 中单元格突出显示的视觉状态。像这样:

- (void)collectionView:(PSUICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    MYCollectionViewCell *cell = (MYCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
    [cell highlight];
}

- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    MYCollectionViewCell *cell = (MYCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
    [cell unhighlight];
}

我不喜欢这种方法的地方在于,它向委托中添加了非常特定于单元格的逻辑。事实上,UICollectionViewCell 通过highlighted 属性独立管理其突出显示状态。

那么,覆盖 setHighlighted: 不是更清洁的解决方案吗?

- (void)setHighlighted:(BOOL)highlighted
{
    [super setHighlighted:highlighted];
    if (highlighted) {
        [self highlight];
    } else {
        [self unhighlight];
    }
}

这种方法比委托方法有什么缺点吗?

【问题讨论】:

    标签: ios ios6 uicollectionview uicollectionviewcell


    【解决方案1】:

    正如文档所述,您可以在单元格突出显示时更改 highlighted 属性。例如,以下代码将在突出显示时使单元格变为红色(虽然不是它的子视图):

    - (void)setHighlighted:(BOOL)highlighted {
        [super setHighlighted:highlighted];
        [self setNeedsDisplay];
    }
    
    - (void)drawRect:(CGRect)rect {
        [super drawRect:rect];
    
        if (self.highlighted) {
            CGContextRef context = UIGraphicsGetCurrentContext();
            CGContextSetRGBFillColor(context, 1, 0, 0, 1);
            CGContextFillRect(context, self.bounds);
        } 
    }
    

    如果你添加这样的东西,背景会变成紫色(红色+不透明的蓝色):

    - (void)collectionView:(UICollectionView *)colView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
        UICollectionViewCell *cell = [colView cellForItemAtIndexPath:indexPath];
        cell.contentView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.5];
    }
    
    - (void)collectionView:(UICollectionView *)colView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath {
        UICollectionViewCell *cell = [colView cellForItemAtIndexPath:indexPath];
        cell.contentView.backgroundColor = nil;
    }
    

    因此您可以同时使用两者(不一定同时更改单元格外观)。不同之处在于,对于委托方法,您还拥有indexPath。它可能用于创建多选(您将与选择委托方法一起使用此方法),在单元格突出显示时显示一些预览,在其他视图中显示一些动画......这个委托有很多设备我认为的方法。

    作为结论,我会将单元格外观留给单元格本身来处理,并使用委托方法让控制器同时制作一些很酷的东西。

    【讨论】:

    • +1 谢谢。我喜欢setNeedsDisplay + drawRect 的方法。也许我会在调用setNeedsDisplay 之前检查self.highlighted != highlighted(在第1 行),以避免不必要的绘图。
    • 在覆盖setHighlighted: 时不需要drawRectsetNeedsDisplay 调用。这样就可以了:self.contentView.backgroundColor = highlighted ? UIColor(white: 217.0/255.0, alpha: 1.0) : nil
    • @bizz84 如果您仔细阅读那里解释的答案。
    • 漂亮的解释我正在寻找这个来澄清!
    【解决方案2】:

    下面列出了两种可能的方法。

    细胞子类化

    如果已经从 UICollectionViewCell 子类化,则更清洁的方法。

    class CollectionViewCell: UICollectionViewCell {
    
        override var highlighted: Bool {
            didSet {
                self.contentView.backgroundColor = highlighted ? UIColor(white: 217.0/255.0, alpha: 1.0) : nil
            }
        }
    }
    

    UICollectionViewDelegate

    不太干净,需要集合视图委托了解单元格的表示逻辑。

    func collectionView(collectionView: UICollectionView, didHighlightItemAtIndexPath indexPath: NSIndexPath) {
    
        if let cell = collectionView.cellForItemAtIndexPath(indexPath) {
            cell.contentView.backgroundColor = UIColor(white: 217.0/255.0, alpha: 1.0) // Apple default cell highlight color
        }
    }
    
    
    func collectionView(collectionView: UICollectionView, didUnhighlightItemAtIndexPath indexPath: NSIndexPath) {
    
        if let cell = collectionView.cellForItemAtIndexPath(indexPath) {
            cell.contentView.backgroundColor = nil
        }
    }
    

    【讨论】:

    • 第一个解决方案很棒。谢谢!
    【解决方案3】:

    注意UICollectionViewCell 有一个selectedBackgroundView 属性。默认情况下,它为零。只需为这个属性创建一个视图,当用户触摸单元格时它就会出现。

    override func awakeFromNib() {
        super.awakeFromNib()
    
        let view = UIView(frame: contentView.bounds)
        view.isUserInteractionEnabled = false
        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.backgroundColor = UIColor(white: 0.94, alpha: 1.0)
        selectedBackgroundView = view
    }
    

    【讨论】:

    • 非常干净的实现,避免了delaysContentTouches 问题。
    • 这会在从详细屏幕返回时在 iOS 13 中的多个单元格上闪烁:/...
    • 没关系,我打电话给collectionView.reloadData() viewDidAppear ??‍♂️
    【解决方案4】:

    高亮单元格就足够了(Swift 4)

    class MyCollectionViewCell: UICollectionViewCell {
    ...
        override var isHighlighted: Bool {
            didSet {
                if isHighlighted {
                    self.contentView.alpha = 0.6
                }
                else {
                    self.contentView.alpha = 1.0
                }
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      嗯...因为所有这些方法都是正确的。我找到了对我来说似乎最简单的方法。只需覆盖 setSelected: 方法(例如更改背景颜色):

      -(void)setSelected:(BOOL)selected{
          self.backgroundColor = selected?[UIColor greenColor]:[UIColor grayColor];
          [super setSelected:selected];
      }
      

      ...它“开箱即用”(即使使用 collectionView.allowsMultipleSelection)

      【讨论】:

      • 如果你有多个单元格并且你上下滚动,它会选择随机单元格,有没有解决方案?
      【解决方案6】:

      直接取自 UICollectionViewCell.h - 覆盖 setSelectedsetHighlighted 是正确的。根据您的情况,您可以考虑将自定义视图分配给 backgroundViewselectedBackgroundView,它们会在选择时自动交换。

      // Cells become highlighted when the user touches them.
      // The selected state is toggled when the user lifts up from a highlighted cell.
      // Override these methods to provide custom UI for a selected or highlighted state.
      // The collection view may call the setters inside an animation block.
      @property (nonatomic, getter=isSelected) BOOL selected;
      @property (nonatomic, getter=isHighlighted) BOOL highlighted;
      
      // The background view is a subview behind all other views.
      // If selectedBackgroundView is different than backgroundView, it will be placed above the background view and animated in on selection.
      @property (nonatomic, retain) UIView *backgroundView;
      @property (nonatomic, retain) UIView *selectedBackgroundView;
      

      【讨论】:

        【解决方案7】:

        Swift 3:(基于 A-Live 的回答)

        import UIKit
        
        class MyCollectionViewCell: UICollectionViewCell {
        
            override var highlighted: Bool {
                didSet {
                    self.setNeedsDisplay()
                }
            }
        
            override func drawRect(rect: CGRect) {
                super.drawRect(rect)
                myImageView.highlighted = self.highlighted
            }
        }
        

        斯威夫特 4

        import UIKit
        
        class MyCollectionViewCell: UICollectionViewCell {
        
            override var isHighlighted: Bool {
                didSet {
                    self.setNeedsDisplay()
                }
            }
        
            override func draw(_ rect: CGRect) {
                super.draw(rect)
                myImageView.isHighlighted = self.isHighlighted
            }
        }
        

        【讨论】:

        • 不需要setNeedsDisplay,不需要绘制矩形。只需将您的逻辑从绘图矩形移动到 didSet。
        猜你喜欢
        • 1970-01-01
        • 2013-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-02
        • 1970-01-01
        • 2015-04-19
        • 2011-04-12
        相关资源
        最近更新 更多