即使在采取措施后,UICollectionView单元重用也会导致问题

时间:2016-01-12 18:46:37

标签: ios swift uicollectionview uicollectionviewcell

我知道这个问题已被多次询问过。

我将UICollectionView与自定义单元格一起使用,这些单元格具有一些属性,最重要的是UISwitch的数组以及UILabel&的数组#39; S。你可以猜到,当我滚动时,标签重叠,开关改变状态。我已经实现了UICollectionViewCell prepareForReuse的方法,我在其中清空这些数组并重置主标签文本。

我尝试将不同答案的解决方案结合起来,并且我已经达到了保留标签的程度,但单元格中的开关状态并非如此。我的下一步是创建一个数组以在删除开关之前保留状态,然后将新创建的开关的on属性设置为索引处的此数组的值。这是有效的,直到我非常快速地滚动并切换之前未选择的单元格被选中(或未选中)。这对我来说是一个巨大的问题。

这是我的collectionView cellForItemAtIndexPath方法:

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

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

        // remove views previously created and create array to preserve the state of switches
       var selectedSwitches: [Bool] = []

       for item: UIView in cell.contentView.subviews {
            if (item.isKindOfClass(UILabel) && !item.isEqual(cell.tekstPitanjaLabel)){
                item.removeFromSuperview()
            }
            if (item.isKindOfClass(UISwitch)){
                selectedSwitches.append((item as! UISwitch).on)
                item.removeFromSuperview()
            }
        }

            // get relevant data needed to place cells programmatically
            cell.tekstPitanjaLabel.text = _pitanja[indexPath.row].getText()
            let numberOfLines: CGFloat = CGFloat(cell.tekstPitanjaLabel.numberOfLines)
            cell.setOdgovori(_pitanja[indexPath.row].getOdgovori() as! [Odgovor])
            var currIndex: CGFloat = 1
            let floatCount: CGFloat = CGFloat(_pitanja.count)
            let switchConstant: CGFloat = 0.8
            let switchWidth: CGFloat = cell.frame.size.width * 0.18
            let heightConstant: CGFloat = (cell.frame.size.height / (floatCount + 2) + (numberOfLines * 4))
            let labelWidth: CGFloat = cell.frame.size.width * 0.9

            for item in _pitanja[indexPath.row].getOdgovori() {

                // create a switch
                let odgovorSwitch: UISwitch = UISwitch(frame: CGRectMake((self.view.frame.size.width - (switchWidth * 2)), currIndex * heightConstant , switchWidth, 10))
                odgovorSwitch.transform = CGAffineTransformMakeScale(switchConstant, switchConstant)
                let switchValue: Bool = selectedSwitches.count > 0 ? selectedSwitches[Int(currIndex) - 1] : false
                odgovorSwitch.setOn(switchValue, animated: false)

                // cast current item to relevant class
                let obj: Odgovor = item as! Odgovor

                // create a label
                let odgovorLabel: UILabel = UILabel(frame: CGRectMake((self.view.frame.size.width / 12), currIndex * heightConstant , labelWidth, 20))
                odgovorLabel.text = obj.getText();
                odgovorLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
                odgovorLabel.font = UIFont(name: (odgovorLabel.font?.fontName)!, size: 15)

                // add to cell
                cell.addSwitch(odgovorSwitch)
                cell.addLabel(odgovorLabel)

                currIndex++
            }

                return cell
    }

我的自定义单元格还实现了addSwitchaddLabel方法,它们将contentView的元素添加为subview

滚动时有什么办法可以保持开关的状态吗?

编辑:根据@Victor Sigler的建议,我创建了一个像这样的二维数组:

var _switchStates: [[Bool]]!

我像这样初始化它:

let odgovori: Int = _pitanja[0].getOdgovori().count
                _switchStates = [[Bool]](count: _pitanja.count, repeatedValue: [Bool](count: odgovori, repeatedValue: false))

我改变了我的方法:

for item: UIView in cell.contentView.subviews {
            if (item.isKindOfClass(UILabel) && !item.isEqual(cell.tekstPitanjaLabel)){
                item.removeFromSuperview()
            }
            if (item.isKindOfClass(UISwitch)){
                _switchStates[indexPath.row][current] = (item as! UISwitch).on
                current++
                selectedSwitches.append((item as! UISwitch).on)
                item.removeFromSuperview()
            }
        }

最后:

 let switchValue: Bool = _switchStates.count > 0 ? _switchStates[indexPath.row][Int(currIndex) - 1] : false

2 个答案:

答案 0 :(得分:1)

首先,正如您在UICollectionView中关于单元格默认行为的问题所说,您需要保存每个单元格的状态,在UISwitch的情况下,您需要保留本地属性中的状态,而不是cellForRowAtIndexPath方法,因为每次重复使用单元格时,此方法都会调用。

首先声明一个数组,你要在这个函数之外保存UISwitch的状态,如下所示:

var selectedSwitches: [Bool] = []

或者您可以声明它,然后在viewDidLoad实例化它,它取决于您,我建议您在viewDidLoad中实例化它,并仅将其声明为此类属性:

var selectedSwitches: [Bool]!

然后,您可以根据UISwitch的状态执行任何操作,当然,保留在更改为onoff时保留。

我希望这对你有所帮助。

答案 1 :(得分:0)

我做到了!

最后我实现了这个:

func collectionView(collectionView: UICollectionView, didEndDisplayingCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
        var current: Int = 0
        var cell = cell as! PitanjeCell
        for item: UISwitch in cell.getOdgovoriButtons() {
            if(current == _switchStates[0].count) { break;}
            _switchStates[indexPath.row][current] = (item).on
            current++
        }
    }

在这个功能中,我保存了状态。

这与prepareForReuse组合:

public override func prepareForReuse() {
        self.tekstPitanjaLabel.text = nil
        self._odgovoriButtons.removeAll()
        self._odgovoriLabels.removeAll()
        self._odgovori.removeAll()
        super.prepareForReuse()
    }

终于做到了!