Does iOS 13 - UIMenu have a bug that doesn't show its image?

1.7k Views Asked by At

Paste the following code into a project:

No image shows next to 'Device Honey' ie the UIMenu However the image shows up next to 'Copy' ie the UIACtion.

Am I doing something wrong? If this is a bug? Is there a workaround?

class ViewController: UIViewController {
    let tableview: UITableView = {
        let tv = UITableView()
        tv.frame = UIScreen.main.bounds

        return tv
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(tableview)
        tableview.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableview.delegate = self
        tableview.dataSource = self
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        if cell.detailTextLabel == nil {
            cell = UITableViewCell(style: .value1, reuseIdentifier: "cell")
        }
        cell.textLabel?.text = "Honey"
        cell.detailTextLabel?.text = "iOS developer"

        return cell
    }

    @available(iOS 13.0, *)
    func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {

        return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { suggestedActions in

            return self.makeContextMenu(for: indexPath)
        })
    }

    @available(iOS 13.0, *)
    func makeContextMenu(for indexPath: IndexPath) -> UIMenu? {

        let copyAction = UIAction(title: "Copy", image: UIImage(systemName: "square.and.arrow.up")) { [weak self] _ in
            guard let self = self else { return }
            let cell = self.tableview.cellForRow(at: indexPath)
            let pasteboard = UIPasteboard.general
            pasteboard.string = cell?.detailTextLabel?.text
        }

        guard let cell = self.tableview.cellForRow(at: indexPath), let title = cell.textLabel?.text else { return nil}
        return UIMenu(title: "Device \(title) ", image: UIImage(systemName: "square.and.arrow.up"), children: [copyAction])
    }
}

enter image description here

In Apple's WWDC demo they're able to do it as shown below:

enter image description here

3

There are 3 best solutions below

12
On BEST ANSWER

A context menu has two parts: the preview and the menu. Both are optional. The "groceries" thing in the Apple screenshot is the preview, not the menu. By default, the cell is snapshotted and the snapshot is displayed as the preview. The menu itself in the Apple screenshot has no image and no title. And that's what you should do too! The last line

return UIMenu(...

...should have no title and no image. This menu, the one that wraps everything else and is returned, is the top-level menu, and it is displayed differently (as your own screenshot shows). It looks best without a title, and it cannot display an image at all. Its job is to wrap everything else and to provide an identifier, and that's all.

You will then get something like this:

enter image description here

0
On

This is more of an important comment

The preview that you're showing in your question is terribly misleading. It looks like the other items in the menu. Yet that Groceries section in your screenshot is a Preview. A much more distinctive preview would be something like this:

enter image description here

Image source

0
On

I have the same question, documentation on UIMenu init(title:image:identifier:options:children:) indicates an image parameter:

image: The image to display next to the menu's title.

It looks like is intended to be used in submenus, as shown in this blog: UIMenu Comprehensive Guide