Чтобы добавить / удалить элементы с панели инструментов, вам необходим делегат на панели инструментов: NSToolbarDelegate.
Вот шаблон для реализации, которую я использую (вероятно, больше, чем вы хотите).
Код Boilerplate для создания элементов панели инструментов различных типов:
struct ToolbarIdentifiers {
static let mainToolbar = NSToolbar.Identifier(stringLiteral: "MainToolbar")
static let navGroupItem = NSToolbarItem.Identifier(rawValue: "NavGroupToolbarItem")
static let shareItem = NSToolbarItem.Identifier(rawValue: "ShareToolBarItem")
static let addItem = NSToolbarItem.Identifier(rawValue: "AddToolbarItem")
static let statusItem = NSToolbarItem.Identifier(rawValue: "StatusToolbarItem")
static let filterItem = NSToolbarItem.Identifier(rawValue: "FilterToolbarItem")
static let sortItem = NSToolbarItem.Identifier(rawValue: "SortToolbarItem")
static let cloudUploadItem = NSToolbarItem.Identifier(rawValue: "UploadToolbarItem")
static let cloudDownloadItem = NSToolbarItem.Identifier(rawValue: "DownloadToolbarItem")
static let leftButtonItem = NSToolbarItem.Identifier(rawValue: "leftButtonToolbarItem")
static let rightButtonItem = NSToolbarItem.Identifier(rawValue: "rightButtonToolbarItem")
static let hideShowItem = NSToolbarItem.Identifier(rawValue: "hideShowToolbarItem")
}
// Base toolbar item type, extended for segmented controls, buttons, etc.
struct ToolbarItem {
let identifier: NSToolbarItem.Identifier
let label: String
let paletteLabel: String
let tag: ToolbarTag
let image: NSImage?
let width: CGFloat
let height: CGFloat
let action: Selector?
weak var target: AnyObject?
var menuItem: NSMenuItem? = nil // Needs to be plugged in after App has launched.
let group: [ToolbarItem]
init(_ identifier: NSToolbarItem.Identifier, label: String = "", tag: ToolbarTag = .separator, image: NSImage? = nil,
width: CGFloat = 38.0, height: CGFloat = 28.0,
action: Selector? = nil, target: AnyObject? = nil, group: [ToolbarItem] = [], paletteLabel: String = "") {
self.identifier = identifier
self.label = label
self.paletteLabel = paletteLabel
self.tag = tag
self.width = width
self.height = height
self.image = image
self.action = action
self.target = target
self.group = group
}
}
// Image button -- creates NSToolbarItem
extension ToolbarItem {
func imageButton() -> NSToolbarItem {
let item = NSToolbarItem(itemIdentifier: identifier)
item.label = label
item.paletteLabel = label
item.menuFormRepresentation = menuItem // Need this for text-only to work
item.tag = tag.rawValue
let button = NSButton(image: image!, target: target, action: action)
button.widthAnchor.constraint(equalToConstant: width).isActive = true
button.heightAnchor.constraint(equalToConstant: height).isActive = true
button.title = ""
button.imageScaling = .scaleProportionallyDown
button.bezelStyle = .texturedRounded
button.tag = tag.rawValue
button.focusRingType = .none
item.view = button
return item
}
}
// Segmented control -- creates NSToolbarItemGroup containing multiple instances of NSToolbarItem
extension ToolbarItem {
func segmentedControl() -> NSToolbarItemGroup {
let itemGroup = NSToolbarItemGroup(itemIdentifier: identifier)
let control = NSSegmentedControl(frame: NSRect(x: 0, y: 0, width: width, height: height))
control.segmentStyle = .texturedSquare
control.trackingMode = .momentary
control.segmentCount = group.count
control.focusRingType = .none
control.tag = tag.rawValue
var items = [NSToolbarItem]()
var iSeg = 0
for segment in group {
let item = NSToolbarItem(itemIdentifier: segment.identifier)
items.append(item)
item.label = segment.label
item.tag = segment.tag.rawValue
item.action = action
item.target = target
control.action = segment.action // button & container send to separate handlers
control.target = segment.target
control.setImage(segment.image, forSegment: iSeg)
control.setImageScaling(.scaleProportionallyDown, forSegment: iSeg)
control.setWidth(segment.width, forSegment: iSeg)
control.setTag(segment.tag.rawValue, forSegment: iSeg)
iSeg += 1
}
itemGroup.paletteLabel = paletteLabel
itemGroup.subitems = items
itemGroup.view = control
return itemGroup
}
}
// Text field -- creates NSToolbarItem containing NSTextField
extension ToolbarItem {
func textfieldItem() -> NSToolbarItem {
let item = NSToolbarItem(itemIdentifier: identifier)
item.label = ""
item.paletteLabel = label
item.tag = tag.rawValue
let field = NSTextField(string: label)
field.widthAnchor.constraint(equalToConstant: width).isActive = true
field.heightAnchor.constraint(equalToConstant: height).isActive = true
field.tag = tag.rawValue
field.isSelectable = false
item.view = field
return item
}
}
// Menu item -- creates an empty NSMenuItem so that user can click on the label
// definitely a work-around till we implement the menus
extension ToolbarItem {
mutating func createMenuItem(_ action: Selector) {
let item = NSMenuItem()
item.action = action
item.target = target
item.title = label
item.tag = tag.rawValue
self.menuItem = item
}
}
/*
* Create specialized toolbar items with graphics, labels, actions, etc
* Encapsulates implementation-specific details in code, because the table-driven version was hard to read.
*/
struct InitializeToolbar {
}
extension InitializeToolbar {
static func navGroupItem(_ action: Selector, segmentAction: Selector, target: AnyObject) -> ToolbarItem {
var group = [ToolbarItem]()
group.append(ToolbarItem(NSToolbarItem.Identifier(rawValue: "BackToolbarItem"), label: "Prev", tag: .navPrev,
image: NSImage(named: NSImage.goBackTemplateName), action: segmentAction, target: target))
group.append(ToolbarItem(NSToolbarItem.Identifier(rawValue: "FwdToolbarItem"), label: "Next", tag: .navNext,
image: NSImage(named: NSImage.goForwardTemplateName), action: segmentAction, target: target))
let item = ToolbarItem(ToolbarIdentifiers.navGroupItem, tag: .navGroup, width: 85, height: 28,
action: action, target: target, group: group, paletteLabel: "Navigation")
return item
}
}
extension InitializeToolbar {
static func hideShowItem(_ action: Selector, segmentAction: Selector, target: AnyObject) -> ToolbarItem {
var group = [ToolbarItem]()
group.append(ToolbarItem(NSToolbarItem.Identifier(rawValue: "HideLeftItem"), label: "", tag: .leftButton,
image: NSImage(named: "leftButton"), action: segmentAction, target: target))
group.append(ToolbarItem(NSToolbarItem.Identifier(rawValue: "HideRightItem"), label: "", tag: .rightButton,
image: NSImage(named: "rightButton"), action: segmentAction, target: target))
let item = ToolbarItem(ToolbarIdentifiers.hideShowItem, tag: .hideShow, width: 85, height: 28,
action: action, target: target, group: group, paletteLabel: "Hide/Show")
return item
}
}
extension InitializeToolbar {
static func addItem(_ action: Selector, target: AnyObject) -> ToolbarItem {
let item = ToolbarItem(ToolbarIdentifiers.addItem, label: "Add", tag: .add, image: NSImage(named: NSImage.addTemplateName), action: action, target: target)
return item
}
}
extension InitializeToolbar {
static func shareItem(_ action: Selector, target: AnyObject) -> ToolbarItem {
let item = ToolbarItem(ToolbarIdentifiers.shareItem, label: "Share", tag: .share, image: NSImage(named: NSImage.shareTemplateName), action: action, target: target)
return item
}
}
extension InitializeToolbar {
static func filterItem(_ action: Selector, target: AnyObject) -> ToolbarItem {
let item = ToolbarItem(ToolbarIdentifiers.filterItem, label: "Filter", tag: .filter, image: NSImage(named: "filter"), action: action, target: target)
return item
}
}
extension InitializeToolbar {
static func sortItem(_ action: Selector, target: AnyObject) -> ToolbarItem {
let item = ToolbarItem(ToolbarIdentifiers.sortItem, label: "Sort", tag: .sort, image: NSImage(named: "sort"), action: action, target: target)
return item
}
}
extension InitializeToolbar {
static func cloudDownloadItem(_ action: Selector, target: AnyObject) -> ToolbarItem {
let item = ToolbarItem(ToolbarIdentifiers.cloudDownloadItem, label: "Down", tag: .cloudDownload, image: NSImage(named: "cloudDownload"), action: action, target: target)
return item
}
}
extension InitializeToolbar {
static func cloudUploadItem(_ action: Selector, target: AnyObject) -> ToolbarItem {
let item = ToolbarItem(ToolbarIdentifiers.cloudUploadItem, label: "Up", tag: .cloudUpload, image: NSImage(named: "cloudUpload"), action: action, target: target)
return item
}
}
extension InitializeToolbar {
static func leftButtonItem(_ action: Selector, target: AnyObject) -> ToolbarItem {
let item = ToolbarItem(ToolbarIdentifiers.leftButtonItem, label: "", tag: .leftButton, image: NSImage(named: "leftButton"), action: action, target: target)
return item
}
}
extension InitializeToolbar {
static func rightButtonItem(_ action: Selector, target: AnyObject) -> ToolbarItem {
let item = ToolbarItem(ToolbarIdentifiers.rightButtonItem, label: "", tag: .rightButton, image: NSImage(named: "rightButton"), action: action, target: target)
return item
}
}
extension InitializeToolbar {
static func textItem() -> ToolbarItem {
return ToolbarItem(ToolbarIdentifiers.statusItem, label: "Watch This Space", tag: .status, width: 300, height: 24)
}
}
Вот класс панели инструментов, который реализует инициализатор и делегат:
/*
* Initializer builds a specialized toolbar.
*/
enum ToolbarTag: Int {
case separator = 1
case navGroup
case navPrev
case navNext
case add
case share
case filter
case sort
case cloudDownload
case cloudUpload
case leftButton
case rightButton
case hideShow
case status
}
class Toolbar: NSObject, NSToolbarDelegate, Actor {
var actorDelegate: ActorDelegate?
var identifier: NSUserInterfaceItemIdentifier?
var toolbarItemList = [ToolbarItem]()
var toolbarItemIdentifiers: [NSToolbarItem.Identifier] { return toolbarItemList.map({ $0.identifier }) }
var toolbarDefaultItemList = [ToolbarItem]()
var toolbarDefaultItemIdentifiers: [NSToolbarItem.Identifier] { return toolbarDefaultItemList.map({ $0.identifier }) }
// Delegate toolbar actions
@objc func controlSentAction(_ sender: Any) {
guard let control = sender as? NSControl else { return }
guard let tag = ToolbarTag(rawValue: control.tag) else { return }
actorDelegate?.actor(self, initiator: control, tag: tag, obj: nil)
}
@objc func segmentedControlSentAction(_ sender: Any) {
guard let segmented = sender as? NSSegmentedControl else { return }
guard let tag = ToolbarTag(rawValue: segmented.tag(forSegment: segmented.selectedSegment)) else { return }
actorDelegate?.actor(self, initiator: segmented, tag: tag, obj: nil)
}
// These don't get called at the moment
@objc func toolbarItemSentAction(_ sender: Any) { ddt("toolbarItemSentAction") }
@objc func menuSentAction(_ sender: Any) { ddt("menuSentAction") }
// Toolbar initialize
init(_ window: Window) {
super.init()
identifier = Identifier.View.toolbar
let toolbar = NSToolbar(identifier: ToolbarIdentifiers.mainToolbar)
toolbar.centeredItemIdentifier = ToolbarIdentifiers.statusItem
// Build the initial toolbar
// Text field
toolbarItemList.append(ToolbarItem(.flexibleSpace))
toolbarItemList.append(InitializeToolbar.textItem())
toolbarItemList.append(ToolbarItem(.flexibleSpace))
// Show/Hide
toolbarItemList.append(InitializeToolbar.hideShowItem(#selector(toolbarItemSentAction), segmentAction: #selector(segmentedControlSentAction), target: self))
// Save initial toolbar as default
toolbarDefaultItemList = toolbarItemList
// Also allow these, just to demo adding
toolbarItemList.append(InitializeToolbar.cloudDownloadItem(#selector(controlSentAction), target: self))
toolbarItemList.append(InitializeToolbar.sortItem(#selector(controlSentAction), target: self))
toolbar.allowsUserCustomization = true
toolbar.displayMode = .default
toolbar.delegate = self
window.toolbar = toolbar
}
deinit {
ddt("deinit", caller: self)
}
}
/*
* Implement NSToolbarDelegate
*/
extension Toolbar {
// Build toolbar
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
guard let item = toolbarItemList.firstIndex(where: { $0.identifier == itemIdentifier }) else { return nil }
switch toolbarItemList[item].identifier {
case ToolbarIdentifiers.navGroupItem, ToolbarIdentifiers.hideShowItem:
return toolbarItemList[item].segmentedControl()
case ToolbarIdentifiers.addItem, ToolbarIdentifiers.shareItem, ToolbarIdentifiers.sortItem, ToolbarIdentifiers.filterItem, ToolbarIdentifiers.cloudUploadItem, ToolbarIdentifiers.cloudDownloadItem,
ToolbarIdentifiers.leftButtonItem, ToolbarIdentifiers.rightButtonItem:
return toolbarItemList[item].imageButton()
case ToolbarIdentifiers.statusItem:
return toolbarItemList[item].textfieldItem()
default:
return nil
}
} // end of toolbar
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return toolbarDefaultItemIdentifiers;
}
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return toolbarItemIdentifiers
}
func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return []
}
func toolbarWillAddItem(_ notification: Notification) {
}
func toolbarDidRemoveItem(_ notification: Notification) {
}
} // End of extension
Начальная панель инструментов:
* +1012 *
Раскрывающийся список настроек, который делает Какао для вас:
![enter image description here](https://i.stack.imgur.com/fLBbn.jpg)
После добавления облачной кнопки:
Надеюсь, что это полезно.
Добавлено для уточнения 28.04.2009:
Мой класс панели инструментов не является подклассом NSToolbar. Его инициализатору передается ссылка на окно, так что в конце он устанавливает панель инструментов окна на созданную им панель инструментов:
init(_ window: Window) {
super.init()
identifier = Identifier.View.toolbar
**** stuff removed for clarity ****
let toolbar = NSToolbar(identifier: ToolbarIdentifiers.mainToolbar)
toolbar.allowsUserCustomization = true
toolbar.displayMode = .default
toolbar.delegate = self
window.toolbar = toolbar
}
Возможно, это сбивает с толку семантику, но она создает панель инструментов и действует как делегат панели инструментов, как вы можете видеть в расширении.
Протокол "Актер" является частью моей системы координации, не важен для построения самой панели инструментов. Мне бы пришлось включить все демонстрационное приложение, чтобы показать это, и я предполагаю, что у вас есть собственный дизайн для передачи действий панели инструментов вашим контроллерам / моделям.
Это приложение Xcode 10.2 / Swift 5, хотя я не думаю, что оно использует какие-либо новые функции Swift 5.