如何以编程方式将自定义 NSToolbarItem 添加到现有工具栏
How to add a custom NSToolbarItem to an existing toolbar programmatically
我很难将自定义 NSToolbarItem 添加到我现有的工具栏。
NSToolbar 是在 NSWindowController 中创建的,然后我有一个以编程方式填充工具栏项的函数,代码为:
public func populateFileToolbarItem(_ toolbar: NSToolbar) -> Void{
let itemId = NSToolbarItem.Identifier("FILE_OPEN")
let index = toolbar.items.count
var toolbarItem: NSToolbarItem
toolbarItem = NSToolbarItem(itemIdentifier: itemId)
toolbarItem.label = String("File")
toolbarItem.paletteLabel = String("Open File")
toolbarItem.toolTip = String("Open file to be handled")
toolbarItem.tag = index
toolbarItem.target = self
toolbarItem.isEnabled = true
toolbarItem.action = #selector(browseFile)
toolbarItem.image = NSImage.init(named:NSImage.folderName)
toolbar.insertItem(withItemIdentifier: itemId, at: index)
}
然后我调用这个函数将工具栏项添加到windowController中现有的工具栏
.......
populateFileToolbarItem((self.window?.toolbar)!)
self.window?.toolbar?.insertItem(withItemIdentifier: NSToolbarItem.Identifier.flexibleSpace, at: (self.window?.toolbar?.items.count)!)
self.window?.toolbar?.insertItem(withItemIdentifier: NSToolbarItem.Identifier.print, at: (self.window?.toolbar?.items.count)!)
print("after toolbaritems were inserted into toolbar. \(String(describing: self.window?.toolbar?.items.count))")
......
控制台打印显示,只有两个工具栏项被添加到工具栏。
.......
after toolbaritems were inserted into toolbar. Optional(2)
并且工具栏中没有显示自定义项。
哪位有经验的,请指教!
对于工具栏中的 add/remove 个项目,您需要工具栏委托:NSToolbarDelegate。
这是我正在使用的实现模板(可能比您想要的多)。
用于创建各种类型的工具栏项的样板代码:
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)
}
}
这是工具栏 class,它实现了初始化器和委托:
/*
* 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({ [=11=].identifier }) }
var toolbarDefaultItemList = [ToolbarItem]()
var toolbarDefaultItemIdentifiers: [NSToolbarItem.Identifier] { return toolbarDefaultItemList.map({ [=11=].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: { [=11=].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
初始工具栏:
自定义下拉菜单,Cocoa 为您做的:
添加云按钮后:
希望这对您有所帮助。
已添加以澄清 2019 年 4 月 28 日:
我的工具栏 class 不是 NSToolbar 子class。它的初始值设定项传递了对 window 的引用,因此最后它将 window 的工具栏设置为其创建的工具栏:
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
}
也许这会混淆语义,但它会创建工具栏并充当工具栏委托,正如您在扩展中看到的那样。
"Actor" 协议是我的协调框架的一部分,对于构建工具栏本身并不重要。我将不得不包括整个演示应用程序来展示这一点,并且我假设您有自己的设计来将工具栏操作传递给您的 controllers/models.
此应用程序是 Xcode 10.2/Swift 5,尽管我认为它没有使用任何新的 Swift 5 功能。
To create a toolbar, you must create a delegate that provides important information:
- A list of default toolbar identifiers. This list is used when reverting to default, and constructing the initial toolbar. The default set of toolbar items can also be specified using toolbar items found in the Interface Builder library.
- A list of allowed item identifiers. The allowed item list is used to construct the customization palette, if the toolbar is customizable.
- The toolbar item for a given item identifier.
例如添加灵活空间、打印和自定义项目:
class MyWindowController: NSWindowController, NSToolbarDelegate {
var toolbarIdentifier = NSToolbarItem.Identifier("FILE_OPEN")
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [NSToolbarItem.Identifier.flexibleSpace, NSToolbarItem.Identifier.print, toolbarIdentifier]
}
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [NSToolbarItem.Identifier.flexibleSpace, NSToolbarItem.Identifier.print, toolbarIdentifier]
}
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
if itemIdentifier == toolbarIdentifier {
let toolbarItem = NSToolbarItem(itemIdentifier: toolbarIdentifier)
toolbarItem.label = String("File")
toolbarItem.paletteLabel = String("Open File")
toolbarItem.toolTip = String("Open file to be handled")
toolbarItem.isEnabled = true
toolbarItem.target = self
toolbarItem.action = #selector(browseFile)
toolbarItem.image = NSImage.init(named:NSImage.folderName)
return toolbarItem
}
else {
return NSToolbarItem(itemIdentifier: itemIdentifier)
}
}
}
也可以在 IB 中添加部分或全部标准 and/or 自定义项目。
我很难将自定义 NSToolbarItem 添加到我现有的工具栏。
NSToolbar 是在 NSWindowController 中创建的,然后我有一个以编程方式填充工具栏项的函数,代码为:
public func populateFileToolbarItem(_ toolbar: NSToolbar) -> Void{
let itemId = NSToolbarItem.Identifier("FILE_OPEN")
let index = toolbar.items.count
var toolbarItem: NSToolbarItem
toolbarItem = NSToolbarItem(itemIdentifier: itemId)
toolbarItem.label = String("File")
toolbarItem.paletteLabel = String("Open File")
toolbarItem.toolTip = String("Open file to be handled")
toolbarItem.tag = index
toolbarItem.target = self
toolbarItem.isEnabled = true
toolbarItem.action = #selector(browseFile)
toolbarItem.image = NSImage.init(named:NSImage.folderName)
toolbar.insertItem(withItemIdentifier: itemId, at: index)
}
然后我调用这个函数将工具栏项添加到windowController中现有的工具栏
.......
populateFileToolbarItem((self.window?.toolbar)!)
self.window?.toolbar?.insertItem(withItemIdentifier: NSToolbarItem.Identifier.flexibleSpace, at: (self.window?.toolbar?.items.count)!)
self.window?.toolbar?.insertItem(withItemIdentifier: NSToolbarItem.Identifier.print, at: (self.window?.toolbar?.items.count)!)
print("after toolbaritems were inserted into toolbar. \(String(describing: self.window?.toolbar?.items.count))")
......
控制台打印显示,只有两个工具栏项被添加到工具栏。
.......
after toolbaritems were inserted into toolbar. Optional(2)
并且工具栏中没有显示自定义项。
哪位有经验的,请指教!
对于工具栏中的 add/remove 个项目,您需要工具栏委托:NSToolbarDelegate。
这是我正在使用的实现模板(可能比您想要的多)。
用于创建各种类型的工具栏项的样板代码:
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)
}
}
这是工具栏 class,它实现了初始化器和委托:
/*
* 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({ [=11=].identifier }) }
var toolbarDefaultItemList = [ToolbarItem]()
var toolbarDefaultItemIdentifiers: [NSToolbarItem.Identifier] { return toolbarDefaultItemList.map({ [=11=].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: { [=11=].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
初始工具栏:
自定义下拉菜单,Cocoa 为您做的:
添加云按钮后:
已添加以澄清 2019 年 4 月 28 日:
我的工具栏 class 不是 NSToolbar 子class。它的初始值设定项传递了对 window 的引用,因此最后它将 window 的工具栏设置为其创建的工具栏:
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
}
也许这会混淆语义,但它会创建工具栏并充当工具栏委托,正如您在扩展中看到的那样。
"Actor" 协议是我的协调框架的一部分,对于构建工具栏本身并不重要。我将不得不包括整个演示应用程序来展示这一点,并且我假设您有自己的设计来将工具栏操作传递给您的 controllers/models.
此应用程序是 Xcode 10.2/Swift 5,尽管我认为它没有使用任何新的 Swift 5 功能。
To create a toolbar, you must create a delegate that provides important information:
- A list of default toolbar identifiers. This list is used when reverting to default, and constructing the initial toolbar. The default set of toolbar items can also be specified using toolbar items found in the Interface Builder library.
- A list of allowed item identifiers. The allowed item list is used to construct the customization palette, if the toolbar is customizable.
- The toolbar item for a given item identifier.
例如添加灵活空间、打印和自定义项目:
class MyWindowController: NSWindowController, NSToolbarDelegate {
var toolbarIdentifier = NSToolbarItem.Identifier("FILE_OPEN")
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [NSToolbarItem.Identifier.flexibleSpace, NSToolbarItem.Identifier.print, toolbarIdentifier]
}
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [NSToolbarItem.Identifier.flexibleSpace, NSToolbarItem.Identifier.print, toolbarIdentifier]
}
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
if itemIdentifier == toolbarIdentifier {
let toolbarItem = NSToolbarItem(itemIdentifier: toolbarIdentifier)
toolbarItem.label = String("File")
toolbarItem.paletteLabel = String("Open File")
toolbarItem.toolTip = String("Open file to be handled")
toolbarItem.isEnabled = true
toolbarItem.target = self
toolbarItem.action = #selector(browseFile)
toolbarItem.image = NSImage.init(named:NSImage.folderName)
return toolbarItem
}
else {
return NSToolbarItem(itemIdentifier: itemIdentifier)
}
}
}
也可以在 IB 中添加部分或全部标准 and/or 自定义项目。