12

in iOS 14, there are new APIs for UIMenu, and it can now be attached to UIBarButtonItem, just like that:

This is my code:

@IBOutlet weak var addButton: UIBarButtonItem! // The button is from the storyboard.

override func viewDidAppear(_ animated: Bool) {
    if #available(iOS 14.0, *) {
        let simpleAction : UIAction = .init(title: "Simple", image: nil, identifier: nil, discoverabilityTitle: nil, attributes: .init(), state: .mixed, handler: { (action) in
            self.addButtonActionPressed(action: .simple)
        })
        
        let advancedAction : UIAction = .init(title: "Advanced", image: nil, identifier: nil, discoverabilityTitle: nil, attributes: .init(), state: .mixed, handler: { (action) in
            self.addButtonActionPressed(action: .advanced)
        })
        
        let actions = [simpleAction, advancedAction]
        
        let menu = UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: actions)
        
        addButton.primaryAction = nil
        addButton.menu = menu
    }
}

But the problem is, that when I press the button, nothing happen. Only when I long-press the button, it shows the menu. I've seen this code on the internet:

button.showsMenuAsPrimaryAction = true

But it won't help me, because Value of type 'UIBarButtonItem' has no member 'showsMenuAsPrimaryAction'

Any ideas how to fix? I'm using Xcode 12.0 beta 4 (12A8179i).

pkamb
  • 33,281
  • 23
  • 160
  • 191
אורי orihpt
  • 2,358
  • 2
  • 16
  • 41
  • I added that to the code. The button is from the Story Board. – אורי orihpt Aug 07 '20 at 10:56
  • I tried to do that in code instead of storyboard, and it worked. I don't know what cause this issue, but I've solved it. Thanks anyway. – אורי orihpt Aug 08 '20 at 15:34
  • Well, you should try to work out what caused the problem. I'm having no deifficulty setting a bar button item's menu to show on tap even when the bar button item is created in the storyboard. – matt Aug 08 '20 at 15:40
  • 1
    Just please note that UIMenu is iOS 13. – vedrano May 11 '21 at 07:59
  • Did you solve this problem? In my case I don't see the menu even after long-pressing the bar button! I set it up in storyboard and since I don't know swift I am not taking the programmatic approach. I have set the button's (not bar button's) showsMenuAsPrimaryAction = true. – AceN Aug 06 '22 at 10:46

3 Answers3

10

I fixed this issue I had. If it's happening to any of you, this what you can do:

  • Try to check if there is any other action to the button. If there is, it won't show the menu as the primary action.

  • If you are using storyboard, use code instead, for example:

    self.navigationItem.rightBarButtonItem = .init(systemItem: .add)
    // Then configure the menu of the item here, by doing:
    navigationItem.rightBarButtonItem!.menu = menu 
    // Replace 'menu' with your menu object.
    

If there are any other tips you know, feel free to edit this question and add them.

matt
  • 515,959
  • 87
  • 875
  • 1,141
אורי orihpt
  • 2,358
  • 2
  • 16
  • 41
3

This is how to create UIMenu for the right UIBarButtonItem

//Initiate an array of UIAction.  
let actions = [
     UIAction(title: "Last month", identifier: UIAction.Identifier("last_montg"), handler: handler),
     UIAction(title: "6 months", identifier: UIAction.Identifier("six_month"), handler: handler),
     UIAction(title: "1 year", identifier: UIAction.Identifier("one_year"), handler: handler)
 ]

//Initiale UIMenu with the above array of actions.  
let menu = UIMenu(title: "menu",  children: actions)

//Create UIBarButtonItem with the initiated UIMenu and add it to the navigationItem.  
let rightBarButton = UIBarButtonItem(title: "", image: UIImage(systemName: "calendar"), menu: menu)
self.navigationItem.rightBarButtonItem = rightBarButton

//handler to intercept event related to UIActions.  
let handler: (_ action: UIAction) -> () = { action in 
  print(action.identifier)
  switch action.identifier.rawValue {
  case "last_month":
    print("last_month")
  case "six_month":
    print("six_month")
  case "one_year":
    print("one_year")
  default:
    break
  }
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
ben khedher mahmoud
  • 421
  • 1
  • 5
  • 11
-1
    private func configureNavBar() {        
        let button = UIButton()
        button.setImage(UIImage(named: "Slider"), for: .normal)
        button.addTarget(self, action: #selector(handleFilterButton(sender: )), for: .touchUpInside)
        
        navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
    }


// selector that's going to tigger on single tap on nav bar button

    @objc private func handleFilterButton(sender: UIButton) {
        let today = UIAction(title: "Toady", state: .mixed) { _ in
            print("today button tapped")
        }
        
        let nextSevenDays = UIAction(title: "Next 7 Days", state: .mixed) { _ in
            print("next seven day button tapped")
        }
        
        let menu = UIMenu(title: "Filter By", children: [today, nextSevenDays])
        sender.showsMenuAsPrimaryAction = true
        sender.menu = menu
    }
אורי orihpt
  • 2,358
  • 2
  • 16
  • 41
  • This code requires the user to first tap on the button. That will result in `handleFilterButton` being called. Then the menu is put in place. Then the user needs to tap the button again for the menu to appear. Why make the user tap twice for the menu to appear? Why not setup the menu immediately? But why even use a UIButton? Why not setup a UIBarButtonItem directly with a menu? There's no need to use UIButton to show a menu. – HangarRash Jul 02 '23 at 16:49
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 08 '23 at 00:45