iOS 11拖放功能是否可以用于UITableView中同时重新排序多个项目/单元格?

12
我知道在使用新的UITableViewDropDelegateUITableViewDragDelegate代理时,可以一次重新排序单个项目/单元格,但是否可能支持处理多个?
例如,在此屏幕截图中,我拿着一个单独的项目:Dragging a single item/cell 将单元格放置到位。
但是,当我选择多个单元格时,会出现“禁止进入”符号,单元格不会重新排序:Multiple selection Drag and Drop with no entry sign 如果从其他应用程序中选择多个项目,则可以正常工作,例如从iMessage中拖动多个消息:Multiple selection Drag and Drop from iMessage 是否可能在仅具有本地项目的情况下重新排序表视图,以便更快地重新排序?
以下是我的代码: UITableViewDragDelegate
extension DotViewController: UITableViewDragDelegate {
    func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
        return [getDragItem(forIndexPath: indexPath)]
    }

    func tableView(_ tableView: UITableView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem] {
        return [getDragItem(forIndexPath: indexPath)]
    }

    func getDragItem(forIndexPath indexPath: IndexPath) -> UIDragItem {
        // gets the item
    }
}

UITableViewDropDelegate

extension DotViewController: UITableViewDropDelegate {
    func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -> Bool {
        return true
    }

    func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {
        return UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
    }

    func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
        // Handles Drop
    }
}

viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.dataSource = self
    tableView.delegate = self
    tableView.dragDelegate = self
    tableView.dropDelegate = self

    tableView.dragInteractionEnabled = true
}
3个回答

2
我尝试使用UICollectionView做同样的事情,然后发现了这个推文链接,由活跃参与拖放开发的Tyler Fox发布,您还可以在与该主题相关的2017-2018 WWDC视频中看到他。
推文内容:
表格和集合视图不支持多项重排序的UI,正确实现相当棘手。但是,在会话期间,您可以使用.unspecified的意图并提供自己的UI,然后将接受删除操作。如果您希望看到API支持它,请提交增强请求。非常感谢您对特定用例的任何详细信息!

1
通过提供所有选定单元格的引用(例如一个section.row的数组)来实现多行重新排序,然后实现tableView:performDropWith:withCoordinator以重新排序它们是可能的(我猜你知道这一点)。
如果您想支持重新排序,可以返回UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath) 的拖放建议,使UIKit使用iOS 11之前的tableView:moveRowAt:to函数,但这只支持单行。
引用:https://developer.apple.com/videos/play/wwdc2017/223/?time=1836

1
你能澄清一下如何实现多行重新排序吗?你是在实现低级别的拖动委托方法而不是 UITableViewDragDelegate 吗? - Geoff Hackworth

0

很抱歉我不能在这里详细说明,但基本上是这样让它工作的:

  1. 我扩展了在StackOverflow上找到的算法,得到了一个可用于重新排序数据源的工作函数。以下是我可能非常低效/不准确的代码示例:

数组中的多个元素重新排序(示例为整数,但可以修改为使用索引路径和数据源类型):

// Multiple element reordering in array
func reorderList(list: Array<Int>, draggedIndices: Array<Int>, targetIndex: Int) -> Array<Int> {

    // Defining the offsets that occur in destination and source indexes when we iterate over them one by one
    var array = list
    var draggedItemOffset = 0
    var targetIndexOffset = 0

    // Items being dragged ordered by their indexPaths
    let orderedDragIndices = draggedIndices.sorted() // Add {$0.row < $1.row for sorting IndexPaths}

    // Items being dragged ordered by their selection order
    var selectionOrderDragItems = [Int]()

    draggedIndices.forEach { (index) in
        selectionOrderDragItems.append(array[index])
    }

    // Reordering the list
    for i in (0...(orderedDragIndices.count - 1)).reversed()  {
        let removedItem = array.remove(at: orderedDragIndices[i] + draggedItemOffset)

        array.insert(removedItem, at: targetIndex + targetIndexOffset)

        if (i - 1 >= 0) && orderedDragIndices[i - 1] >= targetIndex {
            draggedItemOffset += 1
        } else {
            draggedItemOffset = 0
            targetIndexOffset -= 1
        }
    }

    // Right now the dropped items are in the order of their source indexPaths. Returning them back to the order of selection
    for index in Array(targetIndex + targetIndexOffset + 1...targetIndexOffset).sorted(by: >) {
        array.remove(at: index)
    }
    array.insert(contentsOf: selectionOrderDragItems, at: targetIndex + targetIndexOffset + 1)

    return array
}

在dragDelegate和dropDelegate方法中,我使用了单项重排序(UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath))来获得自由动画,其他单元格为放置而腾出空间。但是,当用户点击附加行时,我在拖动会话处于活动状态时使用didSelectRow收集这些附加indexPath。
使用上述选定行的数组,在performDrop代理方法中,我使用(1)中所述的算法重新构建我的数据源并重新加载具有动画的tableview部分。
我进行了一些额外的动画,以向用户展示正在使用panGestureRecognizer和在选择时制作单元格的快照来收集手指下面的行。
请注意,在此过程中我没有使用canMoveRowAt、canEditRowAt或moveRowAt等传统方法。我使用了iOS 11 API中的performDrop方法。
希望对你有所帮助,如果发现算法存在某些失败情况,请回复我。谢谢,祝好运!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接