UICollectionview - 移动项目时闪烁

时间:2018-05-07 16:09:02

标签: swift uicollectionview uicollectionviewcell ios11

我想在我的UICollectionView中重新排序我的单元格。但是当我放下我的项目时,会调用“cellForItemAt”方法,这将导致单元格闪烁(参见下图)。

我该怎么做才能避免这种行为?

提前感谢您的帮助。

enter image description here

 class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!

    private let cellIdentifier = "cell"
    private let cells = [""]

    private var longPressGesture: UILongPressGestureRecognizer!

    override func viewDidLoad() {
        super.viewDidLoad()

        longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.handleLongGesture(gesture:)))
        collectionView.addGestureRecognizer(longPressGesture)
    }

    //Selectors
    @objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
        switch(gesture.state) {

        case .began:
            guard let selectedIndexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else {
                break
            }
            collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
        case .changed:
            collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
        case .ended:
            collectionView.endInteractiveMovement()
        default:
            collectionView.cancelInteractiveMovement()
        }
    }

}

// MARK: - UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
        return true
    }

    func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    }
}

// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 100, height: 100)
    }
}

2 个答案:

答案 0 :(得分:3)

您需要在 perfomBatchUpdates 中调用 endInteractiveMovement

但是,只要触发 endInteractiveMovement ,就会调用 cellForRow 。因此,将刷新单元格并添加新单元格(使用随机颜色扩展进行检查)。为了确保这一点,您需要将selectedCell保存在变量中。并在 endInteractiveMovement 调用时返回该单元格。

在ViewController中声明currentCell

var isEnded: Bool = true
var currentCell: UICollectionViewCell? = nil

在手势开始时将所选单元格存储在变量中&在 performBatchUpdates 中调用 endInteractiveMovement

所以,你的handleLongGesture func如下所示:

//Selectors
@objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {

    switch(gesture.state) {

    case .began:

        guard let selectedIndexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else {
            break
        }

        isEnded = false
        //store selected cell in currentCell variable
        currentCell = collectionView.cellForItem(at: selectedIndexPath)

        collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)

    case .changed:
        collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
    case .ended:

        isEnded = true
        collectionView.performBatchUpdates({
            self.collectionView.endInteractiveMovement()
        }) { (result) in
            self.currentCell = nil
        }

    default:
        isEnded = true
        collectionView.cancelInteractiveMovement()
    }
}

还需要更改 cellForRow

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    if currentCell != nil && isEnded {
        return currentCell!
   } else {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath)
        cell.backgroundColor = .random
        return cell
    }
}

提示

使用随机颜色扩展来更好地进行测试

extension UIColor {
    public class var random: UIColor {
        return UIColor(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0)
    }
}

修改

如果您有多个部分。 让我们采取数组数组

var data: [[String]] = [["1","2"],
                        ["1","2","3","4","5","6","7"],
                        ["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15"]]

然后您需要在重新排序时维护数据

func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {

    print("\(sourceIndexPath) -> \(destinationIndexPath)")

    let movedItem = data[sourceIndexPath.section][sourceIndexPath.item]
    data[sourceIndexPath.section].remove(at: sourceIndexPath.item)
    data[destinationIndexPath.section].insert(movedItem, at: destinationIndexPath.item)

}

答案 1 :(得分:0)

您可以尝试致电

collectionView.reloadItems(at: [sourceIndexPath, destinationIndexPath])

所有更新(拖放动画)完成后。 例如,在performBatchUpdates之后调用它。它将消除闪烁。