3

I have a UIMenuController with a "Delete" menu item on top of a collection view cell which is displayed when the user long presses on a cell with section 1:

enter image description here

@IBAction func handleLongPressOnCell(_ sender: UILongPressGestureRecognizer) {
    let p = sender.location(in: collectionView)
    guard sender.state == .began, let indexPath = self.collectionView.indexPathForItem(at: p), let cell = self.collectionView.cellForItem(at: indexPath) else { return }

    if indexPath.section == 1 {
        let frameInSuperView = collectionView.convert(cell.frame, to: view)
        let deleteItem = UIMenuItem(title: "Delete", action: #selector(deleteCell))
        UIMenuController.shared.menuItems = [deleteItem]
        UIMenuController.shared.setTargetRect(frameInSuperView, in: view)
        becomeFirstResponder()
        UIMenuController.shared.setMenuVisible(true, animated: true)
    }
}

How do I pass the index path of the cell to the function below? I need this information to delete the object from the server.

@objc internal func deleteCell(sender: UIMenuItem) {
    print("delete menu item tapped! print index path of selected collection view cell?")
}
Cesare
  • 9,139
  • 16
  • 78
  • 130

4 Answers4

2

As @mkeremkeskin pointed out, there's an answer to this where he linked.. but that answer is in Objective-C, here you'll find a Swift 4 version.

You can subclass the UIMenuItem and add the indexPath to it! I had to remove some code for it to work in my playground, but you get the idea :)

class CustomMenuItem: UIMenuItem {
    var indexPath: IndexPath?

    convenience init(title: String, action: Selector, indexPath: IndexPath? = nil) {
        self.init(title: title, action: action)

        self.indexPath = indexPath
    }
}

class ViewController {

    func handleLongPressOnCell(_ sender: UILongPressGestureRecognizer) {

        let indexPath = IndexPath(item: 0, section: 1)

        if indexPath.section == 1 {
            let deleteItem = CustomMenuItem(title: "Delete", action: #selector(deleteCell), indexPath: indexPath)
            UIMenuController.shared.menuItems = [deleteItem]
            UIMenuController.shared.setMenuVisible(true, animated: true)
        }
    }

    @objc internal func deleteCell(sender: CustomMenuItem) {
        guard let indexPath = sender.indexPath else { return }

        // Delete item based on indexPath
    }
}
Laffen
  • 2,753
  • 17
  • 29
1

You cannot directly pass the info along with the selector action; instead you should store index path in a member variable which you set in your long-press handler and consume in your delete handler.

private var indexPathForDeleting: IndexPath? = nil

Don't forget to do your housekeeping and clear the variable when it's no longer needed.

dr_barto
  • 5,723
  • 3
  • 26
  • 47
  • thanks, I was trying to avoid to use a member variable because it's not so clean – Cesare Feb 21 '18 at 12:26
  • psst: `= nil` is redundant – Cesare Feb 21 '18 at 12:27
  • The alternative is to extend `UIMenuItem` and attach a handler block to it... but in my experience that's not worth the effort if you only need it once, also subclassing UIKit classes can be more tricky than it looks on first glance. – dr_barto Feb 21 '18 at 12:29
  • Initializing with `nil` is not required, but sometimes explicit code is better then short code... don't know why but I prefer to have it there, gives me a warm feeling of control :) – dr_barto Feb 21 '18 at 12:31
  • actually yeah, this is by far the easiest and most simple way to solve this :) – Cesare Feb 21 '18 at 12:38
1

You can subclass menu item to get the necessary object.

An example has been answered here:

Pass value through UIMenuItem of UIMenuController

mkeremkeskin
  • 644
  • 10
  • 27
0

I solved this type of issue in this way:

let menuController = UIMenuController.shared
menuController.accessibilityHint = String(indexPath.row)


@objc func deleteCell(_ sender: UIMenuController) {
     print("delete menu item tapped! index path? \(sender.accessibilityHint)")
}

I had using swift 4. Hope it will help.

Nomanur
  • 266
  • 5
  • 7