0

I have a stock standard UICollectionView in a UIViewController that is its delegate. However the shouldShowMenuForItemAt func is not called for a long press. I have added a didSelectItemAt func which does get called on clicking a cell to make sure the delegate is indeed wired up correctly.

I also implemented the canPerformAction to return true and performAction in the delegate along with the canPerformAction and canBecomeFirstResponder to return true in my UICollectionViewCell subclass. None of these func's get called for a long press of a cell. Any suggestions?

Chamitha
  • 215
  • 3
  • 11
  • Try the solution from this answer: https://stackoverflow.com/a/39956745/6126595 – Bartosz Kunat Aug 15 '18 at 09:32
  • @BartoszKunat I will give that a go but it seems like an anti pattern. From the documentation this method should be called by the collection view without the need for this hack. – Chamitha Aug 15 '18 at 23:27
  • On my machine, menu handling "just works". No need for any hack. You may be doing something that is interfering with the long press gesture recognizer. – matt Aug 15 '18 at 23:45
  • @matt That was my expectation too. I created a sample project with just a collection view from IB and the func's mentioned above wired up which exhibits this issue. – Chamitha Aug 16 '18 at 10:04
  • So did your sample project work? If not, would it help you to see a working example? – matt Aug 16 '18 at 12:20
  • @matt No the sample did not work. It wouldn't hurt to see a working example! – Chamitha Aug 16 '18 at 23:13
  • Okay, provided a complete working example. – matt Aug 17 '18 at 15:03

1 Answers1

3

The missing piece of the puzzle, which most people seem to miss, is that in order for menus to work (in a collection view or table view), the cell must implement the selector.

Here's a minimal example. Instruction: Make a new project using the Single View App template. Copy this code and paste it into ViewController.swift, so as to replace completely everything in that file. Run. Long press on a green square. Enjoy. (The menu item does nothing; the point is, you will see the menu item appear.)

import UIKit
class Cell : UICollectionViewCell {
    @objc func f(_ : Any) {}
}
class ViewController: UIViewController {
    let cellid = "cellid"
    @nonobjc private let howdy = #selector(Cell.f)
    override func viewDidLoad() {
        super.viewDidLoad()
        let cv = UICollectionView(frame: self.view.bounds, collectionViewLayout: UICollectionViewFlowLayout())
        self.view.addSubview(cv)
        cv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        cv.delegate = self
        cv.dataSource = self
        cv.register(Cell.self, forCellWithReuseIdentifier: cellid)
    }
}
extension ViewController : UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 100
    }
    func collectionView(_ cv: UICollectionView, cellForItemAt ip: IndexPath) -> UICollectionViewCell {
        let cell = cv.dequeueReusableCell(withReuseIdentifier: cellid, for: ip)
        cell.backgroundColor = .green
        return cell
    }
    func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
        let mi = UIMenuItem(title:"Howdy", action:howdy)
        UIMenuController.shared.menuItems = [mi]
        return true
    }

    func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
        return (action == howdy)
    }
    func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {
    }
}
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    Many thanks @matt. In addition to the cell implementing the selector as you mention, I had mistakenly overridden the `canPerformAction` in the view controller instead of implementing the one in the `UICollectionViewDelegate`! – Chamitha Aug 19 '18 at 02:30
  • Oh, nice catch! – matt Aug 19 '18 at 02:32
  • 2
    It is interesting to note that the `performAction` func in the `UICollectionViewDelegate` is not called for custom menu item actions. Instead the selectors in those menu items are called directly. Rather inconvenient to perform tasks on the collection view such as deleting a cell in my case. – Chamitha Aug 20 '18 at 00:06
  • What I do is forward it to the delegate. The implementation in the cell is just a trampoline. – matt Aug 20 '18 at 00:32