Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interface builder segmented controls?

I'm building a toolbar that I want to look at least somewhat like Preview's. I want to create the Previous/Next buttons. The way I THINK it's done is with a segmented control, each has an image in the control. The problem is that I can't figure out how to get the labels underneath. I can get ONE label centered under the whole thing, but labeling the segmented control, But I can't get a Next and a Previous label under each part of the control.

What am I missing here?

thanks.

like image 729
Brian Postow Avatar asked Sep 24 '26 10:09

Brian Postow


1 Answers

As @frédéric-blanc pointed out: the only way to achieve this is with NSToolbarItemGroup programmatically.

The trick is to assign both a view and a subitems array to the NSToolbarItemGroup.

let group = NSToolbarItemGroup(itemIdentifier: NSToolbarItem.Identifier(rawValue: "NavigationGroupToolbarItem"))

let itemA = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(rawValue: "PrevToolbarItem"))
itemA.label = "Prev"
let itemB = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(rawValue: "NextToolbarItem"))
itemB.label = "Next"

let segmented = NSSegmentedControl(frame: NSRect(x: 0, y: 0, width: 85, height: 40))
segmented.segmentStyle = .texturedRounded
segmented.trackingMode = .momentary
segmented.segmentCount = 2
// Don't set a label: these would appear inside the button
segmented.setImage(NSImage(named: NSImage.goLeftTemplateName)!, forSegment: 0)
segmented.setWidth(40, forSegment: 0)
segmented.setImage(NSImage(named: NSImage.goRightTemplateName)!, forSegment: 1)
segmented.setWidth(40, forSegment: 1)

// `group.label` would overwrite segment labels
group.paletteLabel = "Navigation"
group.subitems = [itemA, itemB]
group.view = segmented

If you want to dabble with it, see the sample app code below.

Sample App (Updated for Swift 5.1)

Sample window

This is all you need to test this in a new macOS/Cocoa sample app:

  1. In Xcode, create a new macOS project using default options (Swift, storyboards).
  2. Add a new file called WindowController.swift and paste this code into it.
  3. Set the window controller's class in interface builder to WindowController.
import Cocoa

class WindowController: NSWindowController, NSToolbarDelegate {

    var _toolbar: NSToolbar!

    let toolbarItems: [[String: Any]] = [
        ["title" : "irrelevant :)", "icon": "NSPreferencesGeneral", "identifier": NSToolbarItem.Identifier(rawValue: "NavigationGroupToolbarItem")],
        ["title" : "Share", "icon": NSImage.shareTemplateName, "identifier": NSToolbarItem.Identifier(rawValue: "ShareToolbarItem")],
        ["title" : "Add", "icon": NSImage.addTemplateName, "identifier": NSToolbarItem.Identifier(rawValue: "AddToolbarItem")]
    ]

    var toolbarTabsIdentifiers: [NSToolbarItem.Identifier] {

        return toolbarItems.compactMap { $0["identifier"] as? NSToolbarItem.Identifier }

    }

    override func windowDidLoad() {

        _toolbar = NSToolbar(identifier: "TheToolbarIdentifier")
        _toolbar.allowsUserCustomization = true
        _toolbar.delegate = self
        window?.toolbar = _toolbar

    }

    func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {

        guard let infoDictionary: [String : Any] = toolbarItems.filter({ $0["identifier"] as? NSToolbarItem.Identifier == itemIdentifier }).first
            else { return nil }

        let toolbarItem: NSToolbarItem

        if itemIdentifier == NSToolbarItem.Identifier(rawValue: "NavigationGroupToolbarItem") {

            let group = NSToolbarItemGroup(itemIdentifier: itemIdentifier)

            let itemA = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(rawValue: "PrevToolbarItem"))
            itemA.label = "Prev"
            let itemB = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(rawValue: "NextToolbarItem"))
            itemB.label = "Next"

            let segmented = NSSegmentedControl(frame: NSRect(x: 0, y: 0, width: 85, height: 40))
            segmented.segmentStyle = .texturedRounded
            segmented.trackingMode = .momentary
            segmented.segmentCount = 2
            // Don't set a label: these would appear inside the button
            segmented.setImage(NSImage(named: NSImage.goLeftTemplateName)!, forSegment: 0)
            segmented.setWidth(40, forSegment: 0)
            segmented.setImage(NSImage(named: NSImage.goRightTemplateName)!, forSegment: 1)
            segmented.setWidth(40, forSegment: 1)

            // `group.label` would overwrite segment labels
            group.paletteLabel = "Navigation"
            group.subitems = [itemA, itemB]
            group.view = segmented

            toolbarItem = group
        } else {
            toolbarItem = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(rawValue: itemIdentifier.rawValue))
            toolbarItem.label = (infoDictionary["title"] as? String)!

            let iconImage = NSImage(named: (infoDictionary["icon"] as? String)!)
            let button = NSButton(frame: NSRect(x: 0, y: 0, width: 40, height: 40))
            button.title = ""
            button.image = iconImage
            button.bezelStyle = .texturedRounded
            toolbarItem.view = button
        }

        return toolbarItem
    }

    func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {

        return self.toolbarTabsIdentifiers

    }

    func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {

        return self.toolbarDefaultItemIdentifiers(toolbar)

    }

    func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {

        return self.toolbarDefaultItemIdentifiers(toolbar)

    }

    func toolbarWillAddItem(_ notification: Notification) {

        print("toolbarWillAddItem", (notification.userInfo?["item"] as? NSToolbarItem)?.itemIdentifier ?? "")

    }

    func toolbarDidRemoveItem(_ notification: Notification) {

        print("toolbarDidRemoveItem", (notification.userInfo?["item"] as? NSToolbarItem)?.itemIdentifier ?? "")

    }

}
like image 62
ctietze Avatar answered Sep 26 '26 15:09

ctietze



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!