简体   繁体   English

如果滚动,swift collectionview单元格didSelectItemAtIndexPath显示错误的单元格

[英]swift collectionview cell didSelectItemAtIndexPath shows wrong cell if you scroll

Let's say you set up a bunch of image views inside a UICollectionView's cells (from an array of image names) and make their alpha 0.5 by default when you set up the items. 假设您在UICollectionView的单元格(来自图像名称数组)中设​​置了一堆图像视图,并在设置项目时默认设置为alpha 0.5。

Then you make the image view's alpha to 1.0 in the didSelectItemAtIndexPath func, so it becomes alpha 1 when the user taps. 然后在didSelectItemAtIndexPath函数中将图像视图的alpha设置为1.0,因此当用户点击时它变为alpha 1。

This works when the user taps a cell, but it does not persist if the user scrolls, because the cell is being re-used by the UI on some other level. 这在用户点击单元格时有效,但如果用户滚动则不会保留,因为该单元正在某些其他级别上被UI重用。

The result is another cell farther down the way (when scrolling) becomes alpha 1.0 and the original cell you selected reverts back to its previous alpha 0.5 appearance. 结果是另一个单元格(滚动时)变为alpha 1.0并且您选择的原始单元格将恢复为之前的alpha 0.5外观。

I understand that this is all done to make things more efficient on the device, but I still have not figured out how to make it work properly where the selected item persists. 我知道这一切都是为了在设备上提高效率,但我仍然没有弄清楚如何让它在所选项目持续存在的情况下正常工作。

ANSWER 回答

Apple does provide a selectedBackgroundView for cells that you can use to change the background color, shadow effect, or outline etc. They also allow you to use an image inside the cell with a "default" and "highlighted" state. Apple 确实为可用于更改背景颜色,阴影效果或轮廓等的单元格提供selectedBackgroundView。它们还允许您使用“默认”和“突出显示”状态在单元格内使用图像。

Both of those methods will persist with the selection properly. 这两种方法都会在选择正确的情况下持续存在。

However, if you wish to use attributes or different elements than one of those provided for indicating your selected state, then you must use a separate data model element that includes a reference to the currently selected item. 但是,如果您希望使用属性或不同的元素之一来指示您选择的状态,则必须使用包含对当前所选项的引用的单独数据模型元素。 Then you must reload the viewcontroller data when the user selects an item, resulting in the cells all being redrawn with your selected state applied to one of the cells. 然后,当用户选择一个项目时,您必须重新加载视图控制器数据,从而导致所有单元格被重绘,并将所选状态应用于其中一个单元格。

Below is the jist of the code I used to solve my problem, with thanks to Matt for his patience and help. 下面是我用来解决问题的代码的主旨,感谢Matt的耐心和帮助。

All of this can be located inside your main UICollectionView Controller class file, or the data array and struct can be located inside their own swift file if you need to use it elsewhere in the project. 所有这些都可以位于主UICollectionView Controller类文件中,或者数据数组和结构可以位于自己的swift文件中,如果您需要在项目的其他位置使用它。

Data and data model: 数据和数据模型:

let imagesArray=["image1", "image2", "image3", ...]

struct Model {
    var imageName : String
    var selectedState : Bool

    init(imageName : String, selectedState : Bool = false){
        self.imageName = imageName
        self.selectedState = selectedState
    }
}

Code for the UICollectionView Controller UICollectionView控制器的代码

// create an instance of the data model for images and their status
var model = [Model]()

@IBOutlet weak var collectionView: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()

    // build out a data model instance based on the images array
    for i in 0..<imagesArray.count  {
        model.append(Model(imageName: imagesArray[i]))
        // the initial selectedState for all items is false unless otherwise set
    }

}

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return imagesArray.count
}

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

    // when the collectionview is loaded or reloaded...

    let cell:myCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! myCollectionViewCell

    // populate cells inside the collectionview with images 
    cell.imageView.image = UIImage(named: model[indexPath.item].imageName)

    // set the currently selected cell  (if one exists) to show its indicator styling
    if(model[indexPath.item].selectedState == true){
            cell.imageView.alpha = 1.0
        } else {
            cell.imageView.alpha = 0.5
    }

    return cell        
}

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    // when a cell is tapped...

    // reset all the selectedStates to false in the data model
    for i in 0..<imagesArray.count {
        model[i].selectedState = false
    }

    // set the selectedState for the tapped item to true in the data model
    model[indexPath.item].selectedState = true

    // refresh the collectionView (triggering cellForItemAtIndexPath above)
    self.collectionView.reloadData()

}

but it does not persist if the user scrolls, because the cell is being re-used by the UI on some other level 但是如果用户滚动它并不会持续存在,因为UI会在其他某个级别上重复使用该单元格

Because you're doing it wrong. 因为你做错了。 In didSelect , make no change to any cells . didSelect不对任何细胞进行任何更改 Instead, make a change to the underlying data model , and reload the collection view. 而是,更改基础数据模型 ,并重新加载集合视图。 It's all about your data model and your implementation of cellForItemAtIndexPath: ; 这都是关于你的数据模型和你的cellForItemAtIndexPath:的实现cellForItemAtIndexPath: that is where cells and slots (item and section) meet. 这是单元格和插槽(项目和部分)相遇的地方。

Here's a simple example. 这是一个简单的例子。 We have just one section, so our model can be an array of model objects. 我们只有一个部分,因此我们的模型可以是模型对象的数组。 I will assume 100 rows. 我将假设100行。 Our model object consists of just an image name to go into this item, along with the knowledge of whether to fade this image view or not: 我们的模型对象只包含要进入此项目的图像名称,以及是否淡化此图像视图的知识:

struct Model {
    var imageName : String
    var fade : Bool
}
var model = [Model]()
override func viewDidLoad() {
    super.viewDidLoad()
    for i in 0..<100 {
        // ... configure a Model object and append it to the array
    }
}
override func collectionView(
    collectionView: UICollectionView, 
    numberOfItemsInSection section: Int) -> Int {
        return 100
}

Now, what should happen when an item is selected? 现在,选择项目时会发生什么? I will assume single selection. 我会假设单一选择。 So that item and no others should be marked for fading in our model. 因此,在我们的模型中,该项目和其他项目都不应标记为淡入淡出。 Then we reload the data: 然后我们重新加载数据:

override func collectionView(cv: UICollectionView, 
    didSelectItemAtIndexPath indexPath: NSIndexPath) {
        for i in 0..<100 {model[i].fade = false}
        model[indexPath.item].fade = true
        cv.reloadData()
}

All the actual work is done in cellForItemAtIndexPath: . 所有实际工作都在cellForItemAtIndexPath:完成cellForItemAtIndexPath: And that work is based on the model : 这项工作基于模型

override func collectionView(cv: UICollectionView,
    cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let c = self.collectionView!.dequeueReusableCellWithReuseIdentifier(
            "Cell", forIndexPath: indexPath) as! MyCell
        let model = self.model[indexPath.item]
        c.iv.image = UIImage(named:model.imageName)
        c.iv.alpha = model.fade ? 0.5 : 1.0
        return c
}

You logic is incorrect. 你的逻辑不正确。 didSelectItemAtIndexPath is used to trigger something when a cell is selected. didSelectItemAtIndexPath用于在选择单元格时触发某些内容。 All this function should contain is this: 所有这个功能都应该包含:

let cell:stkCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! stkCollectionViewCell

cell.imageView.alpha = 1.0

selectedIndex = indexPath.item

Then in your cellForItemAtIndexPath function you should have the logic to set the cell because this is where the cells are reused . 然后在您的cellForItemAtIndexPath函数中,您应该具有设置单元格的逻辑,因为这是重用单元格的位置。 So this logic should be in there: 所以这个逻辑应该在那里:

if (indexPath.item == selectedIndex){
    print(selectedIndex)
    cell.imageView.alpha = 1.0
} 
else {
    cell.imageView.alpha = 0.5
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM