iosswiftuikit

How to change the state for an UIAction inside UIMenu at runtime?


How to change the state for an UIAction? The goal is to toggle a state checkmark next to an UIAction inside UIMenu.

enter image description here

Changing a UIAction's state via a reference stored in the view controller does not seem to change the state at all. Am I missing anything?

// View Controller
internal var menuAction: UIAction!

private func generatePullDownMenu() -> UIMenu {
    menuAction = UIAction(
        title: "Foo",
        image: UIImage(systemName: "chevron.down"),
        identifier: UIAction.Identifier("come.sample.action"),
        state:  .on
    ) { _ in self.menuAction.state = .off } // <--- THIS LINE


    let menu = UIMenu(
        title: "Sample Menu",
        image: nil,
        identifier: UIMenu.Identifier("com.sample.menu"),
        options: [],
        children: [menuAction]
    )

    return menu
}

// Inside UI setup code block
let buttonItem = UIBarButtonItem(
    title: "",
    image: UIImage(systemName: "chevron.down"),
    primaryAction: nil,
    menu: generatePullDownMenu()
)

Tried to change the action state from the closure directly and got the "Action is immutable because it is a child of a menu" error. Now I suspect an action object is always an immutable object.

menuAction = UIAction(
    title: "Foo",
    image: UIImage(systemName: "chevron.down"),
    identifier: UIAction.Identifier("come.sample.action"),
    state:  .on
) { action in action.state = .off } // <--- THIS LINE

Solution

  • Replace the entire UIMenu object on state change would do the trick.

    // view controller
    internal var barButton: UIBarButtonItem!
    
    // UI setup function
    barButton = UIBarButtonItem(
        image: UIImage(systemName: "arrow.up.arrow.down.square"),
        primaryAction: nil,
        menu: generatePullDownMenu()
    )
    
    // On state change inside UIAction 
    let actionNextSeen = UIAction(
        title: "foo",
        image: UIImage(systemName: "hourglass", )
        state: someVariable ? .off : .on
    ) { _ in
        someVariable = false
        self.barButton.menu = self.generatePullDownMenu()
    }
    

    REFERENCE

    https://developer.apple.com/forums/thread/653862