ios UICollectionView 选择和取消选择问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15330844/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 22:38:55  来源:igfitidea点击:

UICollectionView Select and Deselect issue

ioscocoa-touchuicollectionview

提问by bworby

So I have a main object that has many images associated with it. An Image is also an object.

所以我有一个主要对象,它有很多与之相关的图像。图像也是一个对象。

Say you have a collection view controller, and in that controller you have

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

cellForItemAtIndexPath

cellForItemAtIndexPath

well based on the main object, if it has the current image associated with it I want to set selected to true. But I want the user to be able to "un-select" that current cell at any time to remove its association with the main object.

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

I find that if you set "selected to true" - if there is an relation between the main object and image in cellForItemAtIndexPath, de-selection is no longer an option.

我发现,如果您将“选择为真”设置为 - 如果 中的主要对象和图像之间存在关系cellForItemAtIndexPath,则不再选择取消选择。

in

didDeselectItemAtIndexPath

and

didSelectItemAtIndexPath

I test with a log to see if they are called. If a cell is set to selected - nether gets called, but If I never set a cell to selected in cellForItemAtIndexPathI can select and deselect all I want.

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

Is this the intended way a collection view is supposed to work? I read the docs and it does not seem to talk about this being so. I interpret the docs to mean it works the way a table view cell would. with a few obvious changes

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

This also shows the controller is set up correct and is using the appropriate delegate methods.... hmmmm

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

回答by user2405793

I had the same issue, ie. setting cell.selected = YESin [UICollectionView collectionView:cellForItemAtIndexPath:]then not being able to deselect the cell by tapping on it.

我有同样的问题,即。设置cell.selected = YES[UICollectionView collectionView:cellForItemAtIndexPath:]那么不能够通过点击它来取消细胞。

Solution for now: I call both[UICollectionViewCell setSelected:]and [UICollectionView selectItemAtIndexPath:animated:scrollPosition:]in [UICollectionView collectionView:cellForItemAtIndexPath:].

解决方案现在:我打电话[UICollectionViewCell setSelected:][UICollectionView selectItemAtIndexPath:animated:scrollPosition:][UICollectionView collectionView:cellForItemAtIndexPath:]

回答by Fjohn

I had a Deselect issue with UICollectionViewand I found that I was not allowing multiple selection on collectionView. So when I was testing I tried always on the same cell and if single selection is ON you can't Deselect a cell already selected.

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

I had to add:

我不得不补充:

myCollectionView.allowsMultipleSelection = YES;

回答by Aneel

Do you have a custom setSelectedmethod in your Cell class? Are you calling [super setSelected:selected]in that method?

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

I had a mysterious problem where, I was using multiple selection, I could not deselect cells once they were selected. Calling the super method fixed the problem.

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

回答by dmlebron

This is kind of old but, since I encounter the same issue using swift I will add my answer. When using:

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

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

The cell didn't get selected at all. But when using:

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

cell.selected = true

It did get selected but I wasn't able to select/deselect the cell anymore.

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

My solution (use both methods):

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

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

When this two methods are called in:

当这两个方法被调用时:

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

It worked perfectly!

它工作得很好!

回答by funct7

I don't know why UICollectionViewis so messy like this compared to UITableViewController... A few things I found out.

我不知道为什么UICollectionView这么乱比UITableViewController……我发现了一些事情。

The reason why setSelected:gets called multiple times is because of the sequence methods get called. The sequence is very similar to that of UITextFieldDelegatemethods.

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

The method collectionView:shouldSelectItemAtIndexPath:is called before the collectionViewactually selects the cell because it's actually asking "should it be selected"?

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

collectionView:didSelectItemAtIndexPath:is in fact called after the collectionViewselects the cell. Hence the name "did select."

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

So this is what is happening in your case (and my case, and I had to wrestle hours over this).

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

A selected cell is touched again by the user to deselect. shouldSelectItemAtIndexPath:is called to check whether the cell should be selected. The collectionViewselects the cell and then didSelectItemAtIndexPathis called. Whatever you do at this point is after the the cell's selectedproperty is set to YES. That's why something like cell.selected = !cell.selectedwon't work.

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

TL;DR - Have your collectionViewdeselect the cell in the delegate method collectionView:shouldSelectItemAtIndexPath:by calling deselectItemAtIndexPath:animated:and return NO.

TL;DR -通过调用和 returncollectionView取消选择委托方法中的单元格。collectionView:shouldSelectItemAtIndexPath:deselectItemAtIndexPath:animated:NO

Short example of what I did:

我所做的简短示例:

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

回答by Trent

Here is my answer for Swift 2.0.

这是我对 Swift 2.0 的回答。

I was able to set the following in viewDidLoad()

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

collectionView.allowsMultipleSelection = true;

then I implemented these methods

然后我实现了这些方法

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

finally

最后

class MyCell : UICollectionViewCell {

    ....

    func toggleSelected ()
    {
        if (selected){
            backgroundColor = UIColor.orangeColor()
        }else {
            backgroundColor = UIColor.whiteColor()
        }
    }

}

回答by Davin

 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
    }

}

Simple Solution i found

我发现的简单解决方案

回答by Aleksandar Vaci?

Living in the age of iOS 9, there are multiple things to check here.

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

  1. Check do you have collectionView.allowsSelectionset to YES
  2. Check do you have collectionView.allowsMultipleSelectionset to YES(if you need that ability)
  1. 检查您是否已collectionView.allowsSelection设置为YES
  2. 检查您是否已collectionView.allowsMultipleSelection设置为YES(如果您需要该功能)

Now comes the fan part. If you listen to Apple and set backgroundColoron the cell.contentViewinstead of cellitself, then you have just hidden its selectedBackgroundViewfrom ever being visible. Because:

现在是风扇部分。如果您听 Apple 并设置backgroundColorcell.contentView而不是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

So if you want to use selectedBackgroundView (which is the one being turned on/off with cell.selectedand selectItemAtIndexPath...) then do this:

因此,如果您想使用 selectedBackgroundView (这是使用cell.selected和打开/关闭的selectItemAtIndexPath...那个),请执行以下操作:

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

and it should work just fine.

它应该可以正常工作。

回答by Ryan Ballantyne

This question is six years old, but I don't care; after landing here and finding that none of the answers solved my specific problem, I eventually hit upon what I consider to be the best answer to this question. So, I'm posting it here for posterity.

这个问题已经六年了,但我不在乎;在登陆这里并发现没有一个答案解决了我的具体问题后,我最终找到了我认为是这个问题的最佳答案。所以,我把它贴在这里供后代使用。

Selecting cells in cellForItemAtIndexPath is problematic because of the way UICollectionView calls that method. It's not just called when initially setting up a collection view. It's called continuously as the collection view is scrolled, because the collection view only ever asks its data source for visible cells, thereby saving a lot of overhead.

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

Because collection views don't keep all of their cells in memory, they need to manage the selected state of their own cells. They don't expect you to provide them with cells whose isSelected property has been set. They expect you to provide them with cells, and they will set the selected property on them if appropriate.

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

This is why Apple cautions you to not set the isSelected property of UICollectionViewCell directly. UICollectionView expects to take care of that for you.

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

SO, the answer is to NOTattempt to select cells in the cellForItemAtIndexPath method. The best place to select cells that you want to be initially selected is in the viewWillAppear method of the UICollectionViewController. In that method, select all desired cells by calling UICollectionView.selectItem(at:animated:scrollPosition:), and DON'Tset isSelected directly on your cells.

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

回答by Fran Sevillano

When calling both[UICollectionViewCell setSelected:]and [UICollectionView selectItemAtIndexPath:animated:scrollPosition:]in [UICollectionView collectionView:cellForItemAtIndexPath:]doesn't work try calling them inside a dispatch_async(dispatch_get_main_queue(), ^{});block.

当调用[UICollectionViewCell setSelected:][UICollectionView selectItemAtIndexPath:animated:scrollPosition:][UICollectionView collectionView:cellForItemAtIndexPath:]不工作尝试调用他们的内部dispatch_async(dispatch_get_main_queue(), ^{});块。

That's what finally fixed it for me.

这就是最终为我修复的原因。