iosswiftuibuttonuiaction

With an iOS 17 "popup type" UIButton, how to set the selected item in code?


Say you have a popup-type button,

enter image description here

@IBOutlet var popupButton: UIButton!
let xx = ["Horse","Dog","Cat","Budgie"]
let opts = xx.map({ fn in
    return UIAction(title: fn) { [weak self] _ in
        self?. ...
    }
})
opts[2].state = .on
popupButton.menu = UIMenu(children: opts)

enter image description here

During construction, you can set the selected item in the manner shown, even that is a bit hokey.

During running of the app, how the heck do you change the selected item, from code?

Unfortunately the only way I know how is use an extension like this, which is hardly elegant:

// Don't do this. See solution found later in the answer
let temp: [UIAction] = thePopupButton.menu!.children as! [UIAction]
temp[3].state = .on
thePopupButton.menu = UIMenu(children: temp)
// Don't do this. See solution found later in the answer

Surely there's some way to change the selected item on a popup UIButton? Does anyone know?

Please, please, note that this question has absolutely nothing to do with action sheets or alert controllers. Look at the first image if you're unfamiliar with the new UIButton possibilities.


Solution

  • Based on the tip by @sweeper, in fact this does seem to work OK.

    Thoroughly tested:

    ///Util for the "popup" type of UIButton
    extension UIButton {
        
        ///For the "popup" type buttons (iOS 14+), set the selected item.
        ///If the button is the wrong type or it's not possible, nothing is done.
        func forceSelectedIndex(_ index: Int) {
                guard (self.menu != nil) else {
                    return print("forceSelectedIndex impossible")
                }
                guard index > 0 && index < self.menu!.children.count else {
                    return print("forceSelectedIndex impossible")
                }
                (self.menu!.children[index] as? UIAction)?.state = .on
        }
    }
    

    I guess that's about all there is to it.

    No need to copy as in my thought example in the question.