UICollectionView - 如果选择了单元格,则不调用 didDeselectItemAtIndexPath

Posted

技术标签:

【中文标题】UICollectionView - 如果选择了单元格,则不调用 didDeselectItemAtIndexPath【英文标题】:UICollectionView - didDeselectItemAtIndexPath not called if cell is selected 【发布时间】:2013-12-04 16:21:12 【问题描述】:

我要做的第一件事是设置选中的单元格。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.selected = YES;
    return cell;

并且单元格被成功选中。 如果用户触摸选定的单元格,则应取消选择该单元格并调用代表。但这永远不会发生。

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath 
    NSLog(@"%s", __PRETTY_FUNCTION__);


- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath 
    NSLog(@"%s", __PRETTY_FUNCTION__);

我知道如果我以编程方式设置选择,则不会调用代表。 委托和数据源已设置。

但是,这个委托被调用:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath 
    NSLog(@"%s", __PRETTY_FUNCTION__);
    return YES;

如果我删除 cell.selected = YES,那么一切正常。有没有人可以解释这种行为?

【问题讨论】:

当您使用 cellforItemAtIndexPath 时,它会为 tableview 中的每个单元格调用,所以基本上您正在尝试设置每个选定的单元格。你想达到什么目的? 【参考方案1】:

问题是,该单元格已被选中,但 UICollectionView 对此一无所知。额外拨打UICollectionView 即可解决问题:

[collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; 

完整代码:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    cell.selected = YES;
    [collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
    return cell;

我保留这个问题是为了帮助可能面临同样问题的人。

【讨论】:

你好@zeiteisen。在我的情况下,当我选择一个单元格didSelectItemAtIndexPath 时被调用,但是当第二次单击单元格时它不会调用didDselectItemAtIndexPath。我想在这两种方法中更改单元格的内容。我该怎么做? 它有效,但我还需要理解为什么需要它:集合视图回调说用户选择了单元格->我要求它重新加载单元格,以便我可以直观地反映更改,那么当它询问单元格时,我需要告诉它(再次)选择它吗?【参考方案2】:

我认为@zeiteisen 提供的解决方案是一种解决方案。实际的事情在于选择模式。在属性cell.selected 中无需设置任何内容。

UICollectionView 名称 allowsMultipleSelectionallowsSelection 有两个属性。

allowsMultipleSelection 默认为NOallowsSelectionYES

如果你只想选择一个单元格,那么代码@初始化看起来像

yourCollectionView.allowsMultipleSelection = NO;
yourCollectionView.allowsSelection = YES; //this is set by default

然后设置将允许您一次只选择一个单元格,不少于不少。 您必须至少选择一个单元格。 除非您选择另一个单元格,否则不会调用 didDeselectItemAtIndexPath。点击已选择的UICollectionViewCell 不会使其取消选择。这是实施背后的政策。

如果你想选择多个单元格,那么代码@the初始化应该如下所示:

yourCollectionView.allowsMultipleSelection = YES;
yourCollectionView.allowsSelection = YES; //this is set by default

然后设置将允许选择多个单元格。此设置允许选择一个或多个UICollectionViewCell。选择的单元格数将是

0 <= number_selected_cell <= total_number_of_cell

这是多个单元格选择背后的策略。

如果您打算使用上述任何一种方法,欢迎您使用 apples api 而不会额外感到头疼,否则您必须找到一种方法来使用您自己的代码或 apple 的 api 潜入您的目标。

【讨论】:

你是对的,简单地说:如果用户再次点击它,集合视图不会自动取消选择已选择的单元格。 allowMultipleSelection 属性文档中的说明是错误的。对我来说,苹果这样实现它是很奇怪的,为了实现“再次点击并取消选择”行为,我们应该自己维护一个选择标志。 这应该被选为正确答案。关键是设置allowMultipleSelection=YES。 我遇到了@Chris 提到的问题。显然这是因为在我的自定义 collectionView 单元格中覆盖了 setIsSelected 而没有调用 super.选择和取消选择按照文档中的说明进行。【参考方案3】:

我有同样的问题。我在表格加载时预先选择了单元格,但我不得不双击一个单元格来取消选择它。 @zeiteisen 的回答帮助了我。但是,我正在使用 Swift 3,所以我想我会在 Swift 中给出答案。

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell

    if /*Test for Selection*/ 
        cell.isSelected = true
        collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .left)
            
    return cell

【讨论】:

【参考方案4】:

将此行添加到您的 didSelectItemAtIndexPath 方法中

[collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:nil]

【讨论】:

【参考方案5】:

在我的例子中,我想改变按钮的背景,换句话说就是集合视图中单元格的背景:

class CustomCVCell: UICollectionViewCell 

override var isSelected: Bool 
        didSet 
            grayBackgroundViewWithImage.image =
                isSelected ? UIImage(named: "") : UIImage()
        
    

在存储集合视图的主类中创建这个变量:

class CustomViewController: UIViewController 

///save the indexPath of last selected cell
private var lastSelectedIndexPath: IndexPath? 

在 viewDidLoad() 中将此值设置为 false:

customCollectionView.allowsMultipleSelection = false

数据源中的更多代码。在我的情况下,应该选择第一个单元格:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCVCell.cellID(),
                                                  for: indexPath) as! CustomCVCell
    
    if indexPath.row == 0 
        lastSelectedIndexPath = indexPath
        cell.isSelected = true
    
    
    //update last select state from lastSelectedIndexPath
    cell.isSelected = (lastSelectedIndexPath == indexPath)
    
    return cell

委托中的更多代码:

///UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    guard lastSelectedIndexPath != indexPath else  return 

         if let index = lastSelectedIndexPath 
            let cell = collectionView.cellForItem(at: index) as! CustomCVCell
            cell.isSelected = false
          

          let cell = collectionView.cellForItem(at: indexPath) as! CustomCVCell
          cell.isSelected = true
    lastSelectedIndexPath = indexPath

【讨论】:

【参考方案6】:

重新加载单元格对我来说是解决方案。我需要的是在单元格中更改图像片刻。我将图像分配给 cellForItemAtIndexPath 中的 imageView,当用户触摸单元格时,我会更改图像并运行 mycode,然后重新加载单元格。

 func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) 
        let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MenuCollectionViewCell
        cell.backgroundImageView.image = UIImage(named: "selected")
        // handle tap events
        collectionView.reloadItemsAtIndexPaths([indexPath])
    

希望对某人有所帮助。

【讨论】:

【参考方案7】:

在我的情况下,它是由使用相同的逻辑引起的

func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool

至于

func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool

以及选择单元格时,突出的是返回错误 - 这阻止了我取消选择该细胞。

希望这会节省一些人的时间 :)

【讨论】:

【参考方案8】:

我遇到了同样的问题,如果您进一步调查,您会发现不仅没有调用 diddiselect,而且单元格也没有感知到触摸本身。为我解决的问题是允许异步执行选择并手动设置 selectItem 。 在 cellForItemAt 内。

       DispatchQueue.main.async 
            cell.isSelected = true
            collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .init())
        

如果您不想看到动作,请在动画上使用 .init() 和 false。如果您不使用 async 块包装它,您将看到 UI 在滚动时跳跃。

【讨论】:

以上是关于UICollectionView - 如果选择了单元格,则不调用 didDeselectItemAtIndexPath的主要内容,如果未能解决你的问题,请参考以下文章

如果使用 PHP 选择了单选按钮(当前),如何在 Mysql 数据库中为出勤值添加 +1?

UICollectionView - 如果选择了单元格,则不调用 didDeselectItemAtIndexPath

如果从 UICollectionView 的其他部分中选择了一个单元格,则取消选择所有其他选择

如果在重新加载期间触摸单元格,则重新加载后无法选择 UICollectionView 单元格

检查是不是选择了单选框[重复]

如何检查是不是使用 JavaScript 选择了单选按钮?