Swift:无法在UICollectionViewController和UITableView

时间:2016-03-10 15:37:13

标签: ios swift uitableview drag-and-drop uicollectionview

之前,我尝试在UITableView中用长按拖动替换标准的苹果重新排序控件(从右侧手柄拖动单元格)。然而,由于长按阻力,我出于某种原因无法将细胞移动到已经没有细胞的部分。现在我正在尝试实现一个函数,用户可以在UICollectionViewController而不是UITableView中的两个部分之间拖动单元格。我实现了长按拖动功能,但出于某种原因我遇到了同样的问题。我如何在这些部分添加一个虚拟单元格,以便它们永远不会是空的,或者有更好的方法来解决这个问题?还有一种方法可以拖动细胞而不必长按吗?

这些是我添加到我的UICollectionViewController类中以启用长按拖动的函数:

override func viewDidLoad() {
    super.viewDidLoad()

    let longPressGesture = UILongPressGestureRecognizer(target: self, action: "handleLongGesture:")
    self.collectionView!.addGestureRecognizer(longPressGesture)
}

func handleLongGesture(gesture: UILongPressGestureRecognizer) {

    switch(gesture.state) {

    case UIGestureRecognizerState.Began:
        guard let selectedIndexPath = self.collectionView!.indexPathForItemAtPoint(gesture.locationInView(self.collectionView)) else {
            break
        }
        collectionView!.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath)
    case UIGestureRecognizerState.Changed:
        collectionView!.updateInteractiveMovementTargetPosition(gesture.locationInView(gesture.view!))
    case UIGestureRecognizerState.Ended:
        collectionView!.endInteractiveMovement()
    default:
        collectionView!.cancelInteractiveMovement()
    }
}

override func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {

    let fromRow = sourceIndexPath.row
    let toRow = destinationIndexPath.row
    let fromSection = sourceIndexPath.section
    let toSection = destinationIndexPath.section

    var item: Item
    if fromSection == 0 {
        item = section1Items[fromRow]
        section1Items.removeAtIndex(fromRow)
    } else {
        item = section2Items[sourceIndexPath.row]
        section2Items.removeAtIndex(fromRow)
    }

    if toSection == 0 {
        section1Items.insert(score, atIndex: toRow)
    } else {
        section2Items.insert(score, atIndex: toRow)
    }
}

override func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
    return true
}

由于

2 个答案:

答案 0 :(得分:4)

要先回答问题的第二部分,请使用UIPanGestureRecogniser而不是UILongPressRecogniser。

关于空白部分,您可以向空白部分添加无可见性的虚拟单元格。在故事板中创建一个没有子视图的原型单元格,并确保它与集合视图具有相同的背景颜色。

如果该部分为空,则需要安排显示此单元格。但是,当只有一个单元格的部分中移动开始时,您还需要添加一个虚拟单元格,否则该部分将在移动过程中折叠,并且用户无法将单元格移回该部分从它开始。

在手势处理程序中,开始移动时添加临时单元格。如果尚未删除拖动完成时也删除单元格(如果单元格实际上没有移动则不调用委托moveItemAtIndexPath方法):

var temporaryDummyCellPath:NSIndexPath?

func handlePanGesture(gesture: UIPanGestureRecognizer) {

    switch(gesture.state) {
    case UIGestureRecognizerState.Began:
        guard let selectedIndexPath = self.collectionView.indexPathForItemAtPoint(gesture.locationInView(self.collectionView)) else {
            break
        }
        if model.numberOfPagesInSection(selectedIndexPath.section) == 1 {
            // temporarily add a dummy cell to this section
            temporaryDummyCellPath = NSIndexPath(forRow: 1, inSection: selectedIndexPath.section)
            collectionView.insertItemsAtIndexPaths([temporaryDummyCellPath!])
        }
        collectionView.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath)
    case UIGestureRecognizerState.Changed:
        collectionView.updateInteractiveMovementTargetPosition(gesture.locationInView(gesture.view!))
    case UIGestureRecognizerState.Ended:
        collectionView.endInteractiveMovement()

        // remove dummy path if not already removed
        if let dummyPath = self.temporaryDummyCellPath {
            temporaryDummyCellPath = nil
            collectionView.deleteItemsAtIndexPaths([dummyPath])
        }

    default:
        collectionView.cancelInteractiveMovement()

        // remove dummy path if not already removed
        if let dummyPath = temporaryDummyCellPath {
            temporaryDummyCellPath = nil
            collectionView.deleteItemsAtIndexPaths([dummyPath])
        }
    }
}

collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int)中,总是返回比模型中项目数多1的内容,或者如果添加了临时虚拟单元格则返回一个附加单元格。

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    // special case: about to move a cell out of a section with only 1 item
    // make sure to leave a dummy cell 
    if section == temporaryDummyCellPath?.section {
        return 2
    }

    // always keep one item in each section for the dummy cell
    return max(model.numberOfPagesInSection(section), 1)
}

collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath)中,如果行等于该部分的模型中的项目数,则分配虚拟单元格。

禁用collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath)中单元格的选择,为虚拟单元格返回false,对其他单元格返回true。

同样禁用collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath)

中单元格的移动

确保目标移动路径仅限于模型中的行:

func collectionView(collectionView: UICollectionView, targetIndexPathForMoveFromItemAtIndexPath originalIndexPath: NSIndexPath, toProposedIndexPath proposedIndexPath: NSIndexPath) -> NSIndexPath
{
    let proposedSection = proposedIndexPath.section
    if model.numberOfPagesInSection(proposedSection) == 0 {
        return NSIndexPath(forRow: 0, inSection: proposedSection)
    } else {
        return proposedIndexPath
    }

}

现在你需要处理最后的动作。更新模型,然后根据需要添加或删除虚拟单元格:

func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)
{
    // move the page in the model
    model.movePage(sourceIndexPath.section, fromPage: sourceIndexPath.row, toSection: destinationIndexPath.section, toPage: destinationIndexPath.row)

    collectionView.performBatchUpdates({

        // if original section is left with no pages, add a dummy cell or keep already added dummy cell
        if self.model.numberOfPagesInSection(sourceIndexPath.section) == 0 {
            if self.temporaryDummyCellPath == nil {
                let dummyPath = NSIndexPath(forRow: 0, inSection: sourceIndexPath.section)
                collectionView.insertItemsAtIndexPaths([dummyPath])
            } else {
                // just keep the temporary dummy we already created
                self.temporaryDummyCellPath = nil
            }
        }

        // if new section previously had no pages remove the dummy cell
        if self.model.numberOfPagesInSection(destinationIndexPath.section) == 1 {
            let dummyPath = NSIndexPath(forRow: 0, inSection: destinationIndexPath.section)
            collectionView.deleteItemsAtIndexPaths([dummyPath])
        }
    }, completion: nil)

}

最后确保虚拟单元格没有可访问性项目,以便在开启语音时跳过它。

答案 1 :(得分:0)

详细信息

  • 来自iOS 11的Xcode 10.2(10E125),Swift 5

想法

我建议处理

func collectionView(_ collectionView: UICollectionView, dragSessionWillBegin session: UIDragSession) {...}
func collectionView(_ collectionView: UICollectionView, dragSessionDidEnd session: UIDragSession) {...}

当您开始拖动时,可以将 临时 单元格添加到空白(或全部)部分的末尾。并且您的空白部分不会为空)。然后,您可以使用标准UIKit api删除单元格。并且在拖动操作结束之前,您可以删除 临时 单元格。

为什么?

我不建议使用手势处理程序,因为:

  1. “骑自行车”的大机会
  2. 该解决方案可能非常耗时且维护困难/昂贵。
  3. 用于处理经过更深入测试的表/集合的标准API

样本

Full sample of drag and drop

enter image description here

更多信息