I'm trying to create a NSPopUpButton with dynamic contents, this is my design:
+-------------+
| None | <-- Static
| Last Item | <-- Static
|-------------| <-- Separator
| History: | <-- Dynamic: "History:" / "No History"
| ... | <-- Dynamic
+-------------+
And here's my code for ViewController.swift
:
class ViewController: NSViewController, NSMenuDelegate {
@objc dynamic var contents: [String] = ["None", "Last Item", ""]
@objc dynamic var selectedIndex: Int = 0
func updateContent() {
// update contents array
}
override func viewDidLoad() {
super.viewDidLoad()
updateContent()
// other code
}
func menuNeedsUpdate(_ menu: NSMenu) {
for (index, item) in menu.items.enumerated() {
if item.title == "" {
menu.items[index] = .separator()
} else if item.title == "History:" || item.title == "No History" {
menu.items[index].isEnabled = false
}
}
}
func menuDidClose(_ menu: NSMenu) {
print(selectedIndex)
}
}
I did the Cocoa Bindings with the interface builder. My NSPopUpButton
's Content Values
is bound to property contents
, Selected Index
is bound to selectedIndex
. I set the ViewController
object to be the delegate for NSPopUpButton
's embedded NSMenu
.
So, there is no problem with NSPopUpButton
's content, but it checks whatever item I select in the NSPopUpButton
and left them checked even if I select something else, eventually it becomes something like this:
And also if I open the menu (NSPopUpButton
) and directly close it by not selecting any item in the menu (click anywhere other than the menu), it automatically selects the first item ("None") regardless of the previously selected item.
Then, I decided to monitor the value for selectedIndex
after closing the menu by implementing menuDidClose(_:)
, it turns out selectedIndex
is exactly what I selected previously (which is correct). This problem persists even after I deleted the binding for selectedIndex
.
This is really weird and doesn't make any sense. Can anyone explain what is happening with this? And how can I properly populate a NSPopUpButton
with a mixture of static and dynamic content?
The issue is caused by menu.items[index] = .separator()
. It replaces the item array of the menu. The itemArray
property of the popup button points to the item array of the menu and this property is not adjusted. The popup button can't find the menu item to switch off the check mark. Replace the menu item with
menu.removeItem(at: index)
menu.insertItem(NSMenuItem.separator(), at: index)
Or put a separator item in the menu in IB and use the Content Placement Tag setting of the binding to insert the bound items below the separator item.