I have a UIMenu in my app that I am populating in a specific order. I want the menu to always appear in the order that I populated it in.
However, the automatic behavior of UIMenu displays the menu with this order as the "priority" order. Meaning, it will sometimes change the order of the list to position the first item (highest priority) closest to the button my menu is opened from.
So with this default behavior, I am seeing my list sometimes displayed in reverse order.
I know UIContextMenu has a .fixed option that can be set using menu.preferredMenuElementOrder. I'm not seeing anything of the sort for UIMenu though.
Any ideas on how I can set my menu order to .fixed or prevent this automatic ordering behavior?
Here's a code snippit to show how I am creating and populating my menu within a custom textfield class:
private var dropdownMenu = UIMenu() {
didSet {
dropdownButton.menu = dropdownMenu
}
}
/// configureDropdownMenu() is used to set the menu contents for your dropdown textfield.
/// - Parameter viewModel: Must set the data of your ViewModel in the View layer. The ViewModel data will be used to populate the dropdown menu.
func configureDropdownMenu(with viewModel: CustomPulldownTextFieldViewModel) {
// Set the field type and the delegate
self.customFieldType = viewModel.dropdownType
self.isEnabled = true
var dropdownOptions: [String: [UIAction]] = [:]
var dropdownChildren: [UIMenuElement] = []
switch viewModel.dropdownType {
case .categorized:
guard let dropdownHeaders = viewModel.pulldownHeaders else {
fatalError("\nFatal Error: Must pass in headers for categorized dropdown type\n")
}
for (index, section) in viewModel.sectionOptions.enumerated() {
let sectionHeader = dropdownHeaders[index]
dropdownOptions[sectionHeader] = section.map { title in
UIAction(title: title, state: .off) { [self] _ in
textField.text = title
self.delegate?.selectedOptionFromList(pulldownField: self, selection: title)
}
}
}
for header in dropdownHeaders {
// Check if the section is not empty before adding it
if let sectionActions = dropdownOptions[header], !sectionActions.isEmpty {
dropdownChildren.append(UIAction(title: header, attributes: [.disabled], state: .off, handler: { _ in }))
dropdownChildren.append(contentsOf: sectionActions)
}
}
let categorizedMenu = UIMenu(options: .singleSelection, children: dropdownChildren)
self.dropdownMenu = categorizedMenu
case .uncategorized:
// Uncategorized list MUST only have one section in sectionOptions
dropdownChildren = viewModel.sectionOptions[0].map { title in
UIAction(title: title, state: .off) { [self] _ in
textField.text = title
self.delegate?.selectedOptionFromList(pulldownField: self, selection: title)
}
}
let uncategorizedMenu = UIMenu(options: .displayInline, children: dropdownChildren)
self.dropdownMenu = uncategorizedMenu
case .none:
fatalError("\nFatal Error: Must set a dropdownType in the view model\n")
}
}
Since this menu is added to a button, you should set the
preferredMenuElementOrder
property of theUIButton
.