How to programmatically trigger UIMenu from UIViewRepresentable
Asked Answered
E

1

6

I tried to create a custom SwiftUI menu (based on UIMenu).

However, I'm unable to trigger the primary action.

It works when I tap on the CustomMenu but the programmatic action is not triggered. What am I doing wrong?

struct CustomMenu: UIViewRepresentable {
    let title: String
    @Binding var isPresented: Bool

    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle(title, for: .normal)
        button.menu = UIMenu(title: "", options: .displayInline, children: [
            UIAction(title: "Item 1", image: UIImage(systemName: "mic"), handler: { _ in }),
            UIAction(title: "Item 2", image: UIImage(systemName: "envelope"), handler: { _ in }),
            UIAction(title: "Item 3", image: UIImage(systemName: "flame.fill"), handler: { _ in }),
            UIAction(title: "Item 4", image: UIImage(systemName: "video"), state: .on, handler: { _ in }),
        ])
        button.showsMenuAsPrimaryAction = true
        return button
    }

    func updateUIView(_ uiView: UIButton, context: Context) {
        if isPresented {
            // neither of .primaryActionTriggered, .menuActionTriggered, .touchUpInside works
            uiView.sendActions(for: .primaryActionTriggered)
        }
    }
}
struct ContentView: View {
    @State var menuPresented = false

    var body: some View {
        VStack {
            Button("Show menu programmatically") {
                menuPresented = true
            }
            CustomMenu(title: "Show menu on tap", isPresented: $menuPresented)
                .fixedSize()
        }
    }
}
Elsyelton answered 13/1, 2021 at 12:11 Comment(7)
I think it is not possible to trigger the context menu programmaticallyGunn
I'm afraid you're right. I've read about the limitations of the context menu. I just hoped that it's possible to present a menu by triggering a button action (so it's not a context menu). Sadly it looks like it doesn't work.Elsyelton
Any news on this?Thermogenesis
any news here on this topic?Andre
@Andre yes, solved! see answer plsChallenging
@Elsyelton Solved, pls see answerChallenging
@Gunn solved1 see answerChallenging
C
0

How to programmatically trigger any item of a UIMenu:

The trick is you have to remember that, in practice, a UIMenuElement is actually a UIAction.

And to remember to send the action from "that button" which is confusing.

Sooo ...

Here, yourButton is a UIButton which you are using as a popup menu:

    for mc in yourButton.menu?.children ?? [] {
        print(" .. actual text of that menu item is", mc.title)
        if let solve = mc as? UIAction {
            print(solve) // debug info on your Xcode console
            solve.performWithSender(yourButton, target: self)
        }
    }

Thus this code for example will trigger each menu item in sequence as a test:

    for i in 0..<(yourButton.menu.menu?.children ?? []).count {
        let mc = (yourButton.menu.menu?.children ?? [])[i]
        if let solve = mc as? UIAction {
            delay(Double(i) * 1.0) {
                print(solve)
                solve.performWithSender(self.yourButton.menu, target: self)
            }
        }
    }

And that's it!

Challenging answered 4/10 at 16:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.