Swift:无法长按拖动单元格到UICollectionViewController和UICollectionViewController中的空节

kb5ga3dv  于 2023-11-16  发布在  Swift
关注(0)|答案(3)|浏览(135)

以前,我试图取代标准的苹果重新排序控制(从右边的句柄拖动单元格)在可扩展视图中长按拖动。但是,由于某种原因,我无法将单元格移动到一个已经没有单元格的部分。现在我试图实现一个功能,用户可以在UICollectionViewController中的两个部分之间拖动单元格,而不是在UICollectionViewController中。我实现了长按拖动功能,但我有同样的问题,出于某种原因.我如何添加一个虚拟单元格的部分,使他们永远不会空,或者有一个更好的方法围绕这一点?也有没有一种方法来拖动单元格,而不必长按?
这些是我添加到我的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
}

字符串
谢谢

fdbelqdn

fdbelqdn1#

要先回答问题的第二部分,请使用UIPanGestureRecogniser而不是UILongPressRecogniser。
对于空的部分,你可以在空的部分中添加一个没有可见性的虚拟单元格。在故事板中创建一个没有子视图的原型单元格,并确保它的背景颜色与集合视图相同。
您需要安排显示此单元格,否则节将是空的。但是,当移动 * 开始 * 时,您还需要在只有1个单元格的节中添加一个虚拟单元格,否则节将在移动过程中折叠,用户无法将单元格移回其开始的节。
在手势处理程序中,在开始移动时添加一个临时单元格。如果单元格尚未被移除,则在拖动完成时也将其移除(如果单元格实际上没有移动,则不会调用委托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)中,如果行等于模型中该部分的项目数,则分配虚拟单元格。
通过为虚拟单元格返回false并为其他单元格返回true来禁用collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath)中的单元格选择。
类似地,禁用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)

}


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

zzlelutf

zzlelutf2#

详细数据

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

创意

我想建议处理

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

字符串
当你开始拖动的时候,你可以在空的(或者全部)区域的末尾添加***临时的***单元格。并且你的空区域不会是空的)。然后你可以使用标准的UIKit API删除你的单元格。并且在拖动结束之前,你可以删除***临时的***单元格。
为什么
我不建议使用gesture handler,因为:
1.大机会“制造自行车”
1.该解决方案可能非常耗时并且维护起来困难/昂贵。
1.用于处理经过更深入测试的表/集合的标准API

示例

Full sample of drag and drop
x1c 0d1x的数据

更多信息

polkgigr

polkgigr3#

我试过使用LongPress或Dummy/Empty Cell解决方案,但它们对我不起作用。

以下是我的解决方案:

如果您有部分标题(SupplementaryView),您可以通过查找最接近的SupplementaryView及其到当前拖动位置的IndexPath来计算正确的IndexPath:

extension CollectionViewController: UICollectionViewDropDelegate {

    func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
        guard let destinationIndexPath = coordinator.destinationIndexPath ?? closestIndexPath(coordinator.session, collectionView) else { return }

        // Do your reordering work here
    }

    private func closestIndexPath(_ session: UIDropSession, _ collectionView: UICollectionView) -> IndexPath? {
        let location = session.location(in: collectionView)

        let indexPaths = collectionView.indexPathsForVisibleSupplementaryElements(ofKind: "SectionHeaderSupplementaryViewIdentifier")
        let supplementaryViews = collectionView.visibleSupplementaryViews(ofKind: "SectionHeaderSupplementaryViewIdentifier")

        var correctDestination: IndexPath?
        collectionView.performUsingPresentationValues {
            correctDestination = closestIndexPath(to: location, using: supplementaryViews, with: indexPaths)
        }
        return correctDestination
    }

    private func closestIndexPath(to point: CGPoint, using views: [UIView], with indexPaths: [IndexPath]) -> IndexPath? {
        guard views.count == indexPaths.count else {
            print("Views and indexPaths arrays must have the same count.")
            return nil
        }

        var minDistance: CGFloat = .greatestFiniteMagnitude
        var closestIndexPath: IndexPath?

        for (index, view) in views.enumerated() {
            let distance = abs(point.y - view.frame.maxY)
            if distance < minDistance {
                minDistance = distance
                closestIndexPath = indexPaths[index]
            }
        }

        return closestIndexPath
    }
}

字符串

相关问题