【问题标题】:UICollectionView Select and Deselect issueUICollectionView 选择和取消选择问题
【发布时间】:2013-02-26 04:08:32
【问题描述】:

所以我有一个主对象,其中有许多与之关联的图像。 Image 也是一个对象。

假设你有一个集合视图控制器,并且在那个控制器中你有

cellForItemAtIndexPath

基于主要对象,如果它具有与之关联的当前图像,我想将其设置为 true。但我希望用户能够随时“取消选择”该当前单元格以删除其与主对象的关联。

我发现如果你将“selected 设置为 true”——如果 cellForItemAtIndexPath 中的主要对象和图像之间存在关系,则取消选择不再是一个选项。

didDeselectItemAtIndexPath

didSelectItemAtIndexPath

我使用日志进行测试以查看它们是否被调用。如果一个单元格设置为选中 - 下界会被调用,但如果我从未在cellForItemAtIndexPath 中将一个单元格设置为选中,我可以选择和取消选择所有我想要的。

这是集合视图应该工作的预期方式吗?我阅读了文档,但似乎并没有谈到这一点。我将文档解释为它以表格视图单元格的方式工作。有一些明显的变化

这也表明控制器设置正确并且正在使用适当的委托方法....嗯

【问题讨论】:

    标签: ios cocoa-touch uicollectionview


    【解决方案1】:

    我有同样的问题,即。在[UICollectionView collectionView:cellForItemAtIndexPath:] 中设置cell.selected = YES 然后无法通过点击它来取消选择单元格。

    现在的解决方案:我在[UICollectionView collectionView:cellForItemAtIndexPath:] 中调用[UICollectionViewCell setSelected:][UICollectionView selectItemAtIndexPath:animated:scrollPosition:] 两个

    【讨论】:

    • 奇怪的是它应该是必要的 - 但它有效..并且它不会导致 didSelectItemAtIntexPath 委托的无限循环..
    • 自从提出这个方案以来,有没有人找到更好的解决方案?此解决方案也适用于我,但这可能是一个错误。
    • 你是圣人!我告诉你的圣人!不确定 ScrollPosition 枚举的具体情况,但它的工作原理。 (Y)
    • 这个真的帮了我大忙。谢谢!!
    • 为你干杯!我认为它需要调用 [UICollectionView selectItemAtIndexPath:animated:scrollPosition:]。谢谢!
    【解决方案2】:

    UICollectionView 出现取消选择问题,我发现我不允许在 collectionView 上进行多项选择。因此,当我测试时,我总是在同一个单元格上尝试,如果单选为 ON,则无法取消选择已选择的单元格。

    我必须补充:

    myCollectionView.allowsMultipleSelection = YES;
    

    【讨论】:

    • 这是正确答案
    • 我猜大多数人错过的原因是因为它没有在 IB 中公开用于集合视图,对吧?我找过,在IB的任何地方都找不到。
    【解决方案3】:

    您的 Cell 类中有自定义的 setSelected 方法吗?你是用那个方法调用[super setSelected:selected]吗?

    我遇到了一个神秘的问题,我在使用多项选择时,一旦选择了单元格,我就无法取消选择它们。调用 super 方法解决了这个问题。

    【讨论】:

    • 在 5 年的艰苦 iOS 编程之后,我无法相信我错过了如此明显的东西。呵呵!
    • 这很容易错过
    【解决方案4】:

    这有点老了,但是由于我在使用 swift 时遇到了同样的问题,所以我将添加我的答案。 使用时:

     collectionView.selectItemAtIndexPath(indexPath, animated: true, scrollPosition: [])
    

    单元格根本没有被选中。但是使用时:

    cell.selected = true
    

    它确实被选中了,但我无法再选择/取消选择单元格。

    我的解决方案(两种方法都用):

    cell.selected = true
    collectionView.selectItemAtIndexPath(indexPath, animated: true, scrollPosition: .None)
    

    当这两个方法被调用时:

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
    

    效果很好!

    【讨论】:

    • 这个修复我的问题。非常感谢。
    • 其他选项对我不起作用,这个完美。
    • 三年半后的完美答案!
    • .None 不是有效的滚动位置?
    • 4 年后,我们到了。在取消选择单元格时也遇到问题。
    【解决方案5】:

    我不知道为什么UICollectionViewUITableViewController 这么乱... 我发现了一些事情。

    setSelected: 被多次调用的原因是因为顺序方法被调用。该序列与UITextFieldDelegate 方法的序列非常相似。

    collectionView:shouldSelectItemAtIndexPath: 方法在 collectionView 实际选择单元格之前被调用,因为它实际上是在询问“应该选择它”吗?

    collectionView:didSelectItemAtIndexPath: 实际上是在collectionView 选择单元格之后调用的。因此名称为“确实选择了”。

    所以这就是你的情况(和我的情况,我不得不为此挣扎几个小时)。

    用户再次触摸选定的单元格以取消选择。调用shouldSelectItemAtIndexPath: 来检查是否应该选择单元格。 collectionView 选择单元格,然后调用 didSelectItemAtIndexPath。无论您此时做什么,都是在单元格的selected 属性设置为YES 之后。这就是为什么 cell.selected = !cell.selected 这样的东西不起作用的原因。

    TL;DR - 通过调用deselectItemAtIndexPath:animated: 让您的collectionView 取消选择委托方法collectionView:shouldSelectItemAtIndexPath: 中的单元格并返回NO

    我所做的简短示例:

    - (BOOL)collectionView:(OPTXListView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
        NSArray *selectedItemIndexPaths = [collectionView indexPathsForSelectedItems];
    
        if ([selectedItemIndexPaths count]) {
            NSIndexPath *selectedIndexPath = selectedItemIndexPaths[0];
    
            if ([selectedIndexPath isEqual:indexPath]) {
                [collectionView deselectItemAtIndexPath:indexPath animated:YES];
    
                return NO;
            } else {
                [collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
    
                return YES;
            }
        } else {
            [collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
    
            return YES;
        }
    }
    

    【讨论】:

    • 独特而正确的答案。没有设置 collectionView.allowsMultipleSelection = YES;
    【解决方案6】:

    这是我对 Swift 2.0 的回答。

    我可以在 viewDidLoad() 中设置以下内容

    collectionView.allowsMultipleSelection = true;
    

    然后我实现了这些方法

    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCell
        cell.toggleSelected()
    }
    
    func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
        let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCell
        cell.toggleSelected()
    }
    

    终于

    class MyCell : UICollectionViewCell {
    
        ....
    
        func toggleSelected ()
        {
            if (selected){
                backgroundColor = UIColor.orangeColor()
            }else {
                backgroundColor = UIColor.whiteColor()
            }
        }
    
    }
    

    【讨论】:

    • 这可行,但我需要更改所选单元格的边框颜色,这在单元格类中不起作用。你能就此提出什么建议吗?
    • @ArpitDhamane 这对我也不起作用,我正在使用 Swift 3 Xcode 8.1。我只能更改“collectionView: UICollectionView, cellForItemAt indexPath:”中的单元格样式
    【解决方案7】:

    这个问题已经六岁了,但我不在乎;在登陆这里并发现没有一个答案可以解决我的具体问题后,我最终找到了我认为是这个问题的最佳答案。因此,我将其发布在这里以供后代使用。

    在 cellForItemAtIndexPath 中选择单元格是有问题的,因为 UICollectionView 调用该方法的方式。它不只是在最初设置集合视图时调用。它在集合视图滚动时连续调用,因为集合视图只向其数据源询问可见单元格,从而节省了大量开销。

    由于集合视图不会将所有单元格都保存在内存中,因此它们需要管理自己单元格的选定状态。他们不希望您向他们提供已设置 isSelected 属性的单元格。他们希望您为他们提供单元格,如果合适,他们会在其上设置 selected 属性。

    这就是为什么 Apple 提醒您不要直接设置 UICollectionViewCell 的 isSelected 属性。 UICollectionView 会为您解决这个问题。

    所以,答案是尝试在 cellForItemAtIndexPath 方法中选择单元格。选择您希望最初选择的单元格的最佳位置是在 UICollectionViewController 的 viewWillAppear 方法中。在该方法中,通过调用 UICollectionView.selectItem(at:animated:scrollPosition:) 选择所有需要的单元格,然后 DON'T 直接在您的单元格上设置 isSelected。

    【讨论】:

      【解决方案8】:
       func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
      
          let cell = collectionView.cellForItemAtIndexPath(indexPath)
          if cell?.selected == true{
              cell?.layer.borderWidth = 4.0
               cell?.layer.borderColor = UIColor.greenColor().CGColor
          }   
      }func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
          let cell = collectionView.cellForItemAtIndexPath(indexPath)
          if cell?.selected == false{
                  cell?.layer.borderColor = UIColor.clearColor().CGColor
          }
      
      }
      

      我找到的简单解决方案

      【讨论】:

        【解决方案9】:

        我正在使用自定义单元子类,对我来说,我只需在子类中的prepareForReuse() 中设置self.selected = false

        【讨论】:

          【解决方案10】:

          生活在 iOS 9 时代,这里有很多事情需要检查。

          1. 检查是否将collectionView.allowsSelection 设置为YES
          2. 检查您是否将 collectionView.allowsMultipleSelection 设置为 YES(如果您需要该功能)

          现在是风扇部分。 如果您听 Apple 并在cell.contentView 上设置backgroundColor 而不是cell 本身,那么您只是将其selectedBackgroundView 隐藏起来而无法看到。因为:

          (lldb) po cell.selectedBackgroundView
          <UIView: 0x7fd2dae26bb0; frame = (0 0; 64 49.5); autoresize = W+H; layer = <CALayer: 0x7fd2dae26d20>>
          
          (lldb) po cell.contentView
          <UIView: 0x7fd2dae22690; frame = (0 0; 64 49.5); gestureRecognizers = <NSArray: 0x7fd2dae26500>; layer = <CALayer: 0x7fd2dae1aca0>>
          
          (lldb) pviews cell
          <MyCell: 0x7fd2dae1aa70; baseClass = UICollectionViewCell; frame = (0 0; 64 49.5); clipsToBounds = YES; hidden = YES; opaque = NO; layer = <CALayer: 0x7fd2dae1ac80>>
             | <UIView: 0x7fd2dae26bb0; frame = (0 0; 64 49.5); autoresize = W+H; layer = <CALayer: 0x7fd2dae26d20>>
             | <UIView: 0x7fd2dae22690; frame = (0 0; 64 49.5); gestureRecognizers = <NSArray: 0x7fd2dae26500>; layer = <CALayer: 0x7fd2dae1aca0>>
             |    | <UIView: 0x7fd2dae24a60; frame = (0 0; 64 49.5); clipsToBounds = YES; alpha = 0; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x7fd2dae1acc0>>
             |    | <UILabel: 0x7fd2dae24bd0; frame = (0 0; 64 17.5); text = '1'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fd2dae240c0>>
             |    | <UILabel: 0x7fd2dae25030; frame = (0 21.5; 64 24); text = '1,04'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fd2dae25240>>
          
          (lldb) po cell.contentView.backgroundColor
          UIDeviceRGBColorSpace 0.4 0.4 0.4 1
          

          因此,如果您想使用 selectedBackgroundView(这是通过 cell.selectedselectItemAtIndexPath... 打开/关闭的视图),请执行以下操作:

          cell.backgroundColor = SOME_COLOR;
          cell.contentView.backgroundColor = [UIColor clearColor];
          

          它应该可以正常工作。

          【讨论】:

          • 你从哪里得到pviews?我在 Xcode 7.2.1 lldb-340.4.119.1 上得到一个 error: Unrecognized command 'pview'.
          • pviews cell 只是 po [cell recursiveDescription] 调用的快捷方式,您可以在 lldb 控制台中的任何视图上执行此操作
          • 正确。为了在集合视图单元格上使用选定的背景视图,只在单元格背景颜色上设置颜色,而不是内容视图。
          【解决方案11】:

          我不知道我是否理解这个问题,但选定的状态是按单元格设置的,并且将包括单元格内的所有子视图。您没有解释“一个主要对象有许多与之相关联的图像”是什么意思。在子视图中关联?或者你到底是什么意思?

          对我来说这听起来像是一个设计问题。也许您需要一个 UIView 子类,其中包含您需要拥有的任何关联对象;然后可以将该子类设置为内容视图。例如,我会在有图像、描述和与图像相关的录音的情况下这样做。所有都在子类中定义,然后这些子类中的每一个都成为单个单元格的内容视图。

          我还使用了将相关图像排列到包含它们的文件夹的方法。在这种设置下,文件夹和图像每个都有一个子类,并且任何一个都可以作为内容视图附加到单元格(这些作为单个实体存储在核心数据中)。

          也许你可以进一步解释你的问题?

          【讨论】:

          • 有没有人使用过集合视图,在 cellForItemAtIndexPath 中将 selected 设置为 YES 并且能够取消选择该单元格?我已经建立了两个测试项目并且都工作相同。一旦你在 cellForItemAtIndexPath 中将 selected 设置为 true,就不会再次调用取消选择。
          • 不,我认为你做不到。如果我在 celLForItemAtIndexPath 中将一个单元格设置为选中,则项目的行为与从集合视图的初始显示中选中的一样(即选中的背景);但是,如果我查看 [collectionView indexPathsForSelectedItems] 的值,我会得到 nil——这表明我无法通过通常的方式取消选择它。您也许可以设置 cell.selected == NO 的值,因为我没有尝试过。但是如果你不能得到所选项目的索引路径,那肯定会让集合视图更难处理。
          • 这看起来不奇怪吗?我可能想错了,但我肯定会看到很多用途,允许某些单元格以选中状态开始,并且能够被取消选中。
          • 看起来确实很奇怪,但可能有一些原因。您是否尝试过使用 selectItemAtIndexPath: ?
          • 是的,我可以让它以这种方式工作或突出显示一个单元格而不选择它。但随后第一次触摸什么也不做(因为那是初始选择),然后它会取消选择并重新选择。
          【解决方案12】:

          你看过了吗:

          - (BOOL)collectionView:(PSTCollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath;
          

          请更清楚地说明您的问题,也许我们可以追根溯源。我刚刚花了一些时间在 UICollectionView 上。

          我认为您的问题可能源于以下混淆:如果您以编程方式设置 cell.selected = YES,则未调用 didSelectItemAtIndexPath: 的原因是因为仅在 collectionView 本身负责单元格选择时使用(例如。通过水龙头)。

          【讨论】:

            【解决方案13】:

            当在 [UICollectionView collectionView:cellForItemAtIndexPath:] 中调用 both [UICollectionViewCell setSelected:][UICollectionView selectItemAtIndexPath:animated:scrollPosition:] 不起作用时,尝试在 dispatch_async(dispatch_get_main_queue(), ^{}); 中调用它们 块。

            这就是最终为我解决的问题。

            【讨论】:

              【解决方案14】:

              Collectionview 选择,取消选择问题解决了,当我这样做时。在viewDidLoad 中,添加collViewRiskPreferences.allowsMultipleSelection = false 并添加以下内容

              func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
              
                      guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier:"MyCell", for: indexPath) as? MyCell else { return UICollectionViewCell() }
                      
                      cell.setupCellStyle(isSelected: false)
                      
                      return cell
                  }
              
              func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
                      guard let cell = collectionView.cellForItem(at: indexPath) else { return }
                      cell.setupCellStyle(isSelected: true)
                  }
                  
                  func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
                      guard let cell = collectionView.cellForItem(at: indexPath) else { return }
                      cell.setupCellStyle(isSelected: false)
                  }
              

              class MyCell: UICollectionViewCell {
                      func setupCellStyle(isSelected: Bool) {
                          if(isSelected) {
                              self.contentView.backgroundColor = UIColor.blue
                          } else {
                              self.contentView.backgroundColor = UIColor.green
                          }
                      }
                  }
              

              【讨论】:

                【解决方案15】:

                最好通过设置 backgroundView 和选定的背景视图来处理单元格选择和取消选择。我建议确保在 layoutSubviews 方法中正确设置这两个视图的框架(如果您通过 IB 设置选定视图和背景视图)。

                不要忘记将 contentView(如果有)的背景颜色设置为清除,以便显示正确的背景视图。

                永远不要直接设置单元格的选择(即通过 cell.selected = YES),请在集合视图中使用为此目的设计的方法。尽管我同意这些信息在指南中有些分散,但文档中对此进行了明确解释。

                您不需要直接在 collectionView 数据源中查看单元格的背景颜色。

                另外,作为最后一点,如果您在单元格的类中实现它们,请不要忘记调用 [super prepareForReuse] 和 [super setSelected:selected],因为您可能会阻止单元格的超类执行单元格选择.

                如果您需要进一步澄清这个问题,请联系我。

                【讨论】:

                  猜你喜欢
                  • 2019-12-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-08-26
                  • 2019-01-13
                  相关资源
                  最近更新 更多