Using long press gesture to reorder cells in tableview?

前端 未结 6 543
灰色年华
灰色年华 2021-02-04 03:35

I want to be able to reorder tableview cells using a longPress gesture (not with the standard reorder controls). After the longPress is recognized I want the tableView to essent

相关标签:
6条回答
  • 2021-02-04 03:48

    So essentially you want the "Clear"-like row reordering right? (around 0:15)

    This SO post might help.

    Unfortunately I don't think you can do it with the present iOS SDK tools short of hacking together a UITableView + Controller from scratch (you'd need to create each row itself and have a UITouch respond relevant to the CGRect of your row-to-move).

    It'd be pretty complicated since you need to get the animation of the rows "getting out of the way" as you move the row-to-be-reordered around.

    The cocoas tool looks promising though, at least go take a look at the source.

    0 讨论(0)
  • 2021-02-04 03:54

    Sure there's a way. Call the method, setEditing:animated:, in your gesture recognizer code, that will put the table view into edit mode. Look up "Managing the Reordering of Rows" in the apple docs to get more information on moving rows.

    0 讨论(0)
  • 2021-02-04 04:07

    There's a great Swift library out there now called SwiftReorder that is MIT licensed, so you can use it as a first party solution. The basis of this library is that it uses a UITableView extension to inject a controller object into any table view that conforms to the TableViewReorderDelegate:

    extension UITableView {
    
        private struct AssociatedKeys {
            static var reorderController: UInt8 = 0
        }
    
        /// An object that manages drag-and-drop reordering of table view cells.
        public var reorder: ReorderController {
            if let controller = objc_getAssociatedObject(self, &AssociatedKeys.reorderController) as? ReorderController {
                return controller
            } else {
                let controller = ReorderController(tableView: self)
                objc_setAssociatedObject(self, &AssociatedKeys.reorderController, controller, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                return controller
            }
        }
    
    }
    

    And then the delegate looks somewhat like this:

    public protocol TableViewReorderDelegate: class {
    
        // A series of delegate methods like this are defined:
        func tableView(_ tableView: UITableView, reorderRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
    
    }
    

    And the controller looks like this:

    public class ReorderController: NSObject {
    
        /// The delegate of the reorder controller.
        public weak var delegate: TableViewReorderDelegate?
    
        // ... Other code here, can be found in the open source project
    
    }
    

    The key to the implementation is that there is a "spacer cell" that is inserted into the table view as the snapshot cell is presented at the touch point, so you need to handle the spacer cell in your cellForRow:atIndexPath: call:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let spacer = tableView.reorder.spacerCell(for: indexPath) {
            return spacer
        }
        // otherwise build and return your regular cells
    }
    
    0 讨论(0)
  • 2021-02-04 04:09

    They added a way in iOS 11.

    First, enable drag interaction and set the drag and drop delegates.

    Then implement moveRowAt as if you are moving the cell normally with the reorder control.

    Then implement the drag / drop delegates as shown below.

    tableView.dragInteractionEnabled = true
    tableView.dragDelegate = self
    tableView.dropDelegate = self
    
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { }
    
    extension TableView: UITableViewDragDelegate {
    func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
            return [UIDragItem(itemProvider: NSItemProvider())]
        }
    } 
    
    extension TableView: UITableViewDropDelegate {
        func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {
    
            if session.localDragSession != nil { // Drag originated from the same app.
                return UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
            }
    
            return UITableViewDropProposal(operation: .cancel, intent: .unspecified)
        }
    
        func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
        }
    }
    
    0 讨论(0)
  • 2021-02-04 04:09

    Swift 3 and no third party solutions

    First, add these two variables to your class:

    var dragInitialIndexPath: IndexPath?
    var dragCellSnapshot: UIView?
    

    Then add UILongPressGestureRecognizer to your tableView:

    let longPress = UILongPressGestureRecognizer(target: self, action: #selector(onLongPressGesture(sender:)))
    longPress.minimumPressDuration = 0.2 // optional
    tableView.addGestureRecognizer(longPress)
    

    Handle UILongPressGestureRecognizer:

    // MARK: cell reorder / long press
    
    func onLongPressGesture(sender: UILongPressGestureRecognizer) {
      let locationInView = sender.location(in: tableView)
      let indexPath = tableView.indexPathForRow(at: locationInView)
    
      if sender.state == .began {
        if indexPath != nil {
          dragInitialIndexPath = indexPath
          let cell = tableView.cellForRow(at: indexPath!)
          dragCellSnapshot = snapshotOfCell(inputView: cell!)
          var center = cell?.center
          dragCellSnapshot?.center = center!
          dragCellSnapshot?.alpha = 0.0
          tableView.addSubview(dragCellSnapshot!)
    
          UIView.animate(withDuration: 0.25, animations: { () -> Void in
            center?.y = locationInView.y
            self.dragCellSnapshot?.center = center!
            self.dragCellSnapshot?.transform = (self.dragCellSnapshot?.transform.scaledBy(x: 1.05, y: 1.05))!
            self.dragCellSnapshot?.alpha = 0.99
            cell?.alpha = 0.0
          }, completion: { (finished) -> Void in
            if finished {
              cell?.isHidden = true
            }
          })
        }
      } else if sender.state == .changed && dragInitialIndexPath != nil {
        var center = dragCellSnapshot?.center
        center?.y = locationInView.y
        dragCellSnapshot?.center = center!
    
        // to lock dragging to same section add: "&& indexPath?.section == dragInitialIndexPath?.section" to the if below
        if indexPath != nil && indexPath != dragInitialIndexPath {
          // update your data model
          let dataToMove = data[dragInitialIndexPath!.row]
          data.remove(at: dragInitialIndexPath!.row)
          data.insert(dataToMove, at: indexPath!.row)
    
          tableView.moveRow(at: dragInitialIndexPath!, to: indexPath!)
          dragInitialIndexPath = indexPath
        }
      } else if sender.state == .ended && dragInitialIndexPath != nil {
        let cell = tableView.cellForRow(at: dragInitialIndexPath!)
        cell?.isHidden = false
        cell?.alpha = 0.0
        UIView.animate(withDuration: 0.25, animations: { () -> Void in
          self.dragCellSnapshot?.center = (cell?.center)!
          self.dragCellSnapshot?.transform = CGAffineTransform.identity
          self.dragCellSnapshot?.alpha = 0.0
          cell?.alpha = 1.0
        }, completion: { (finished) -> Void in
          if finished {
            self.dragInitialIndexPath = nil
            self.dragCellSnapshot?.removeFromSuperview()
            self.dragCellSnapshot = nil
          }
        })
      }
    }
    
    func snapshotOfCell(inputView: UIView) -> UIView {
      UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
      inputView.layer.render(in: UIGraphicsGetCurrentContext()!)
      let image = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()
    
      let cellSnapshot = UIImageView(image: image)
      cellSnapshot.layer.masksToBounds = false
      cellSnapshot.layer.cornerRadius = 0.0
      cellSnapshot.layer.shadowOffset = CGSize(width: -5.0, height: 0.0)
      cellSnapshot.layer.shadowRadius = 5.0
      cellSnapshot.layer.shadowOpacity = 0.4
      return cellSnapshot
    }
    
    0 讨论(0)
  • 2021-02-04 04:12

    You can't do it with the iOS SDK tools unless you want to throw together your own UITableView + Controller from scratch which requires a decent amount of work. You mentioned not relying on 3rd party solutions but my custom UITableView class can handle this nicely. Feel free to check it out:

    https://github.com/bvogelzang/BVReorderTableView

    0 讨论(0)
提交回复
热议问题