Home > Software design >  Why do I have to click a UIButton twice to get the menu to open?
Why do I have to click a UIButton twice to get the menu to open?

Time:01-02

I have a UIButton with a UIMenu associated with it. When I run the app I have to click the button twice to see the menu. Only the first time. If I click the button again the menu opens up right away. Tried both in the simulator and on the actual device. Running Xcode 14.2 and Swift 5.

I tried marking the button with its role as primary. I tried marking the button with its state already selected. Not sure what else to try. Also, tried restarting Xcode just in case it was some bug in Xcode.

    @IBAction func oxygenButtonPressed(_ sender: UIButton) {
        let twolpm = UIAction(title: "2 LPM") { (action) in
            self.dateFormatter.dateFormat = "HH:mm:ss"
            self.codeLog.append(CodeLog(event: (sender.titleLabel?.text)!, time: (self.dateFormatter.string(from: self.date)), note: "2LPM"))}
        let fourlpm = UIAction(title: "4 LPM") { (action) in
            self.dateFormatter.dateFormat = "HH:mm:ss"
            self.codeLog.append(CodeLog(event: (sender.titleLabel?.text)!, time: (self.dateFormatter.string(from: self.date)), note: "4LPM"))}
        let sixlpm = UIAction(title: "6 LPM") { (action) in
            self.dateFormatter.dateFormat = "HH:mm:ss"
            self.codeLog.append(CodeLog(event: (sender.titleLabel?.text)!, time: (self.dateFormatter.string(from: self.date)), note: "6LPM"))}
        let eightlpm = UIAction(title: "8 LPM") { (action) in
            self.dateFormatter.dateFormat = "HH:mm:ss"
            self.codeLog.append(CodeLog(event: (sender.titleLabel?.text)!, time: (self.dateFormatter.string(from: self.date)), note: "8LPM"))}
        let tenlpm = UIAction(title: "10 LPM") { (action) in
            self.dateFormatter.dateFormat = "HH:mm:ss"
            self.codeLog.append(CodeLog(event: (sender.titleLabel?.text)!, time: (self.dateFormatter.string(from: self.date)), note: "10LPM"))}
        let fifteenlpm = UIAction(title: "15 LPM") { (action) in
            self.dateFormatter.dateFormat = "HH:mm:ss"
            self.codeLog.append(CodeLog(event: (sender.titleLabel?.text)!, time: (self.dateFormatter.string(from: self.date)), note: "15LPM"))}
        let oxygenmenu = UIMenu(title: "Oxygen Menu", options: .displayInline, children:
                                    [twolpm, fourlpm, sixlpm, eightlpm, tenlpm, fifteenlpm])
        sender.showsMenuAsPrimaryAction = true
        sender.menu = oxygenmenu
    }

CodePudding user response:

When I run the app I have to click the button twice to see the menu.

Yup. Because that's exactly what your code says should happen.

Think about it. Your action method runs when the button is tapped. Your action method adds the menu.

So the first time you tap the button, there was no menu, but the action method runs and adds it — and that's all.

The second time you tap the button, the button already has a menu, because the first tap added it; and so that menu now opens.

CodePudding user response:

You need to set the button's menu and showsMenuAsPrimaryAction properties before the button is ever tapped. So you probably want to do it in your view controller's viewDidLoad method. Then (in your storyboard) you should remove the button's connection to the oxygenButtonPressed method, and remove the oxygenButtonPressed method entirely.

class MyViewController: UIViewController {
    let date = ...
    let codeLog = ...
    @IBOutlet var oxygenButton: UIButton!   
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm:ss"
        
        let actions: [UIAction] = [2, 4, 6, 8, 10, 15].map { lpm in
            let title = "\(lpm) LPM"
            return UIAction(title: title) { [weak self] _ in
                guard let self = self else { return }
                self.codeLog.append(CodeLog(
                    event: title,
                    time: dateFormatter.string(from: self.date),
                    note: "\(lpm)LPM"
                ))
            }
        }
        
        oxygenButton.menu = UIMenu(
            title: "Oxygen Menu",
            options: .displayInline,
            children: actions
        )
        oxygenButton.showsMenuAsPrimaryAction = true
    }
}

CodePudding user response:

According to this link:

If you want your UIMenu to appear immediately, you need to set the showsMenuAsPrimaryAction to true, otherwise the menu will appear after long press.

So create the menu and attach it to the button in viewDidLoad. Then set the button’s showsMenuAsPrimaryAction property to true.

  • Related