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?) {
}
}