Is it possible to use iOS 11 Drag and Drop to reorder multiple items/cells at a time in UITableView?

后端 未结 3 1428
借酒劲吻你
借酒劲吻你 2021-01-05 05:45

I know it\'s possible to reorder a single item/cell at a time when using the new UITableViewDropDelegate and UITableViewDragDelegate delegates but

3条回答
  •  萌比男神i
    2021-01-05 06:43

    I'm afraid I cannot go into much detail here, but here's basically how I was able to get it to work:

    1. I extended an algorithm I found on StackOverflow to get a working function that I could use to reorder my datasource. Here's my possibly super-inefficient/inaccurate code for this:

    Multiple element reordering in array (example is for ints but can be modified to use indexpaths and datasource type:

    // Multiple element reordering in array
    func reorderList(list: Array, draggedIndices: Array, targetIndex: Int) -> Array {
    
        // 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
    }
    
    1. In the dragDelegate and dropDelegate methods, I used single item reordering (UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)) to get the free animation where the other cells make space for a drop. However, as the user tapped on additional rows, I collected these additional indexPaths using didSelectRow while the drag session was active.

    2. Using the above array of selected rows, in the performDrop delegate method, I used the algorithm described in (1) to restructure my datasource and reload the tableview section with animation.

    3. I did some additional animation to show the user that rows are being collected under the finger using panGestureRecognizer and making a snapshot of the cell upon selection.

    4. Note that I did not use canMoveRowAt or canEditRowAt or moveRowAt or other traditional methods in this process. I used the performDrop method from the iOS 11 APIs.

    Hope it helps, and do reply if you find that the algorithm has some cases where it fails. Thanks and good luck!

提交回复
热议问题