如何以编程方式将自定义NSToolbarItem添加到现有工具栏

时间:2019-04-27 07:17:33

标签: swift macos toolbar items

我很难在现有工具栏上添加自定义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)

工具栏中没有显示自定义项目。

任何人都有经验,请指教!

2 个答案:

答案 0 :(得分:1)

要从工具栏中添加/删除项目,您需要工具栏委托: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)
    }
}

这是工具栏类,它实现初始化程序和委托:

/*
 * 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

初始工具栏:

enter image description here

可定制为您提供的定制下拉菜单:

enter image description here

添加云按钮后:

enter image description here 希望这会有所帮助。

添加以澄清2019年4月28日:

我的工具栏类不是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功能。

答案 1 :(得分:0)

How Toolbars Work

  

要创建工具栏,必须创建一个提供重要信息的委托:

     
      
  • 默认工具栏标识符的列表。还原为默认设置并构造初始工具栏时,将使用此列表。也可以使用在Interface Builder库中找到的工具栏项目来指定默认的工具栏项目集。
  •   
  • 允许的项目标识符列表。如果工具栏是可自定义的,则允许的项目列表用于构造自定义选项板。
  •   
  • 给定项目标识符的工具栏项目。
  •   

例如,添加一个flexibleSpace,打印和自定义项目:

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中添加一些或所有标准和/或自定义项目。