How to Create AppKit Palette Menus in Objective-C

Apple introduced palette menus in the WWDC 2023 session What’s new in AppKit. Despite being introduced three years ago, this API isn’t mentioned often. The relevant API lives in NSMenu in the NSPaletteMenus category. This brief tutorial demonstrates how to use palette menus in your macOS apps using modern Objective-C. The code samples in this article assume your app targets macOS 14.0 or later.

What is a Palette Menu?

A palette menu is an NSMenu whose items are presented horizontally instead of vertically, making it useful for compact groups of related choices: colors, tags, flags, drawing tools, annotation filters, ratings, priorities, and other visual options.

The Palette Menu Objective-C API

@interface NSMenu (NSPaletteMenus)
 
// Creates a palette menu displaying user-selectable color 
// tags using the provided array of colors and optional titles.
+ (instancetype)paletteMenuWithColors:(NSArray<NSColor *> *)colors 
                               titles:(NSArray<NSString *> *)itemTitles 
                     selectionHandler:(nullable void (^)(NSMenu *menu))onSelectionChange API_AVAILABLE(macos(14.0)) NS_REFINED_FOR_SWIFT;
 
// Creates a palette menu displaying user-selectable color tags
// using the provided template image, tinted using the specified
// array of colors.
+ (instancetype)paletteMenuWithColors:(NSArray<NSColor *> *)colors 
                               titles:(NSArray<NSString *> *)itemTitles 
                        templateImage:(NSImage *)image 
                     selectionHandler:(nullable void (^)(NSMenu *menu))onSelectionChange API_AVAILABLE(macos(14.0)) NS_REFINED_FOR_SWIFT; 
 
// The presentation style of the menu. 
@property NSMenuPresentationStyle presentationStyle API_AVAILABLE(macos(14.0));
 
// The selection mode of the menu.
@property NSMenuSelectionMode selectionMode API_AVAILABLE(macos(14.0));
 
// The menu items that are selected. 
@property (copy) NSArray<NSMenuItem *> *selectedItems API_AVAILABLE(macos(14.0));
 
@end

Unfortunately designing a palette menu in Interface Builder is not supported. There are two main ways to create a palette menu:

1) Use AppKit’s color-palette convenience constructors.
2) Create a normal NSMenu and set its presentationStyle to NSMenuPresentationStylePalette.

Example 1: A Simple Color Tag Palette

Here is a compact color picker for assigning a tag color:

// Helper method that builds the color tag picker.
- (NSMenuItem *)tagColorMenuItem
{
    NSArray<NSColor *> *colors = @[NSColor.systemRedColor,
		                   NSColor.systemOrangeColor,
		                   NSColor.systemYellowColor,
		                   NSColor.systemGreenColor,
		                   NSColor.systemBlueColor,
		                   NSColor.systemPurpleColor,
		                   NSColor.systemGrayColor];
 
     NSArray<NSString *> *titles = @[@"Red",
	                             @"Orange",
	                             @"Yellow",
	                             @"Green",
	                             @"Blue",
	                             @"Purple",
	                             @"Gray"];
 
 
     NSMenu *paletteMenu = [NSMenu paletteMenuWithColors:colors
		                                  titles:titles
		                        selectionHandler:^(NSMenu *menu) 
     {		
         NSMenuItem *selectedItem = menu.selectedItems.firstObject;
	 NSInteger selectedIndex = (selectedItem) ? [menu indexOfItem:selectedItem] : NSNotFound;
	 if (selectedIndex != NSNotFound)
         {
	    NSColor *selectedColor = colors[selectedIndex];
	    NSLog(@"Selected color: %@",selectedColor);
	    // TODO: Apply the selected color here.
	 }
      }];
 
      paletteMenu.selectionMode = NSMenuSelectionModeSelectOne;
 
      // Wrap the paletteMenu in an NSMenuItem.	
      NSMenuItem *parentItem = [[NSMenuItem alloc] initWithTitle:@"Tag Color" action:nil keyEquivalent:@""];
      parentItem.submenu = paletteMenu;
 
      return parentItem;
}

To display the palette menu we simply add the item returned from the -tagColorMenuItem method to an NSMenu and present it like any other menu. The code below demonstrates how to do this:

// -showPaletteMenu: here is the action method of an NSButton.
-(void)showPaletteMenu:(NSButton*)sender
{
    NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Palette Menus"];
    [menu addItemWithTitle:@"Palette Menus" action:nil keyEquivalent:@""];
    [menu addItem:[self tagColorMenuItem]]; // <-- This line adds the palette menu.
 
    NSPoint location = NSMakePoint(NSMidX(sender.bounds) - (menu.size.width / 2.0),
		                   NSMinY(sender.bounds) + (menu.size.height));
 
    [menu popUpMenuPositioningItem:menu.itemArray.lastObject 
	                atLocation:location 
		            inView:sender];
}

The code above gives us the following result:

Tag selector palette menu in AppKit.

The titles are not displayed in the palette, but they are still important for accessibility features such as VoiceOver.

Example 2: A Palette with a Template Image

The second convenience constructor (+paletteMenuWithColors:titles:templateImage: selectionHandler:) lets you provide a template image. This is useful when a colored symbol communicates the choice better than a plain color dot. For example we can create a flag color picker with the following code:

- (NSMenuItem *)flagColorMenuItem
{
     NSArray<NSColor *> *colors = @[NSColor.systemRedColor,
		                    NSColor.systemOrangeColor,
		                    NSColor.systemYellowColor,
		                    NSColor.systemGreenColor,
		                    NSColor.systemBlueColor];
 
     NSArray<NSString *> *titles = @[@"Red Flag",
		                     @"Orange Flag",
		                     @"Yellow Flag",
		                     @"Green Flag",
		                     @"Blue Flag"];
 
     NSImage *flagImage = [NSImage imageWithSystemSymbolName:@"flag.fill" accessibilityDescription:@"Flag"];
 
      __weak PMViewController *weakSelf = self;
      NSMenu *paletteMenu = [NSMenu paletteMenuWithColors:colors
		                                   titles:titles
		                            templateImage:flagImage
		                         selectionHandler:^(NSMenu *menu) 
       {
	    __strong PMViewController *strongSelf = weakSelf;
	    if (strongSelf == nil) { return; }
 
            NSMenuItem *selectedItem = menu.selectedItems.firstObject;
	    if (selectedItem == nil) { return; }
 
	    NSUInteger index = [menu.itemArray indexOfObject:selectedItem];
	    if (index == NSNotFound) { return; } // This would be unexpected.
 
	    [strongSelf applyFlagColor:colors[index] title:titles[index]];
      }];
 
     paletteMenu.selectionMode = NSMenuSelectionModeSelectOne;
 
     NSMenuItem *parentItem = [[NSMenuItem alloc] initWithTitle:@"Flag Color"
	                                                 action:nil
		                                  keyEquivalent:@""];
     parentItem.submenu = paletteMenu;
 
     return parentItem;
}
 
- (void)applyFlagColor:(NSColor *)color title:(NSString *)title
{
    NSLog(@"Selected flag color %@ : %@",color, title);
    // TODO: Apply the color.
}
Flag selector palette menu in AppKit.

Bonus Tip: In this sample a stub method for -applyFlagColor:title: is shown. Note that the selection handler does not capture self directly to call the –applyFlagColor:title: method. This matters if the view controller keeps a strong reference to the menu, for example if the menu is to be reused in subsequent presentations. The menu owns its selection handler block, and blocks strongly capture Objective-C objects by default. If the block captured self, the ownership graph could become view controller -> menu -> block -> view controller, creating a retain cycle.

To avoid a retain cycle, assign self to a weak variable before creating the block, then promote it to a local strong variable inside the block. The local strong reference keeps the controller alive for the duration of the handler after it has been retrieved. The weak/strong pattern makes the lifetime behavior explicit and avoids the object disappearing halfway through the handler.

Example 3: A Manually Created Tool Picker Palette Menu

The palette menu class constructors are convenient, but they are not the only way to create a palette. To manually create a palette menu, instantiate an NSMenu instance and set its presentationStyle to NSMenuPresentationStylePalette.

menu.presentationStyle = NSMenuPresentationStylePalette;

Below is an example of how you can create a tool picker palette menu:

- (NSMenuItem *)toolPaletteMenuItem
{
     NSMenu *toolMenu = [[NSMenu alloc] initWithTitle:@"Tool"];
     toolMenu.presentationStyle = NSMenuPresentationStylePalette;
     toolMenu.selectionMode = NSMenuSelectionModeSelectOne;
 
     NSArray<NSDictionary<NSString *, NSString *> *> *tools = @[
		@{ @"title": @"Select", @"symbol": @"cursorarrow" },
		@{ @"title": @"Pen",    @"symbol": @"pencil" },
		@{ @"title": @"Text",   @"symbol": @"textformat" },
		@{ @"title": @"Shape",  @"symbol": @"square.on.circle" },
		@{ @"title": @"Erase",  @"symbol": @"eraser" }
     ];
 
     NSMenuItem *initialSelectedItem = nil;
 
    for (NSDictionary<NSString *, NSString *> *tool in tools) {
	NSString *title = tool[@"title"];
	NSString *symbolName = tool[@"symbol"];
 
	NSImage *image = [NSImage imageWithSystemSymbolName:symbolName
                         	   accessibilityDescription:title];
	image.template = YES;
 
	// Give all menu items in the toolMenu the same target action.
 	NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title
			                              action:@selector(selectToolFromMenuItem:)
			                       keyEquivalent:@""];
	item.target = self;
	item.image = image;
	item.representedObject = title;
 
	[toolMenu addItem:item];
 
	if ([self.currentToolName isEqualToString:title])
	{
	   initialSelectedItem = item;
	}
     }
 
     if (initialSelectedItem != nil)
     {
	 toolMenu.selectedItems = @[ initialSelectedItem ];
     }
 
     NSMenuItem *parentItem = [[NSMenuItem alloc] initWithTitle:@"Tool"
                                                   	 action:nil
		                                  keyEquivalent:@""];
     parentItem.submenu = toolMenu;
 
     return parentItem;
}
 
- (void)selectToolFromMenuItem:(NSMenuItem *)sender
{
     NSString *toolName = sender.representedObject;
     self.currentToolName = toolName;
     NSLog(@"Selected tool: %@", toolName);
}
A tool picker created using a palette menu in AppKit.

In this example, a __weak self variable is not required because selection is handled with the standard target/action pattern rather than a block. The menu item invokes selectToolFromMenuItem: on its target (a weak reference), and there is no selection-handler block capturing the view controller.

Where Palette Menus Fall Short

Palette menus are useful, but they are not always the best choice. They work best when the user is choosing from a small, fixed set of options. Once the interaction becomes more complex, you may have to embed a custom view inside a NSMenuItem or choose a different AppKit control.

Palette menus also do not provide room for explanation. A horizontal row of icons or colors is compact, but that compactness comes at the cost of context. If each option needs a description, preview, secondary value, keyboard shortcut explanation, or multiple lines of text, the palette layout cannot represent that kind of interface.

Another thing to watch for is item count. Palette menus are best for small groups. Five colors, four annotation modes, or three priority levels work well. As the number of items grows, the palette becomes harder to scan, harder to fit, and less menu-like. If you have a large collection of options, use a standard menu or build your own dedicated picker.

The color convenience constructors are intentionally specialized. They work well when your menu items can be described by a simple list of colors and titles, with an optional shared template image. If each item needs its own image, represented object, validation behavior, or command handling, skip the convenience constructors and build the menu items manually, as shown in Example 3.

Finally, be careful not to use palette menus just because they look modern. A vertical menu is often better for destructive actions, rarely used choices, or options that need keyboard discoverability. Palette menus shine when the choices are visual, compact, and frequently used. If the user has to stop and decode what each item means, the palette is probably the wrong interface.

How to Convert Decimal to Hexadecimal

Decimal and hexadecimal are two number systems that are widely used in the fields of computer science and mathematics. Decimal is a base-10 number system that uses the digits 0-9, while hexadecimal is a base-16 number system that uses the digits 0-9 and the letters A-F. In this tutorial, we will show you how to convert decimal to hexadecimal.

Step 1: Divide the Decimal Number by 16


The first step in converting a decimal number to a hexadecimal number is to divide the decimal number by 16. Write down the quotient and remainder of the division.

For example, let’s convert the decimal number 4096 to hexadecimal.

4096 ÷ 16 = 256
Quotient = 256
Remainder = 0

Step 2: Convert the Remainder to Hexadecimal


The next step is to convert the remainder from the previous step to hexadecimal. To do this, you can use the following conversion chart:

Decimal Hexadecimal
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 A
11 B
12 C
13 D
14 E
15 F

In our example, the remainder is 0, so the hexadecimal equivalent is also 0.

Step 3: Repeat the Process Until the Quotient is 0


The next step is to repeat the previous two steps until the quotient is 0. In our example, we have:

256 ÷ 16 = 16
Quotient = 16
Remainder = 0

16 ÷ 16 = 1
Quotient = 1
Remainder = 0

1 ÷ 16 = 0
Quotient = 0
Remainder = 1

Step 4: Write the Hexadecimal Equivalent


Once the quotient is 0, you can write down the hexadecimal equivalent by writing the remainders from the last step in reverse order. In our example, the remainders are 0, 0, 0, and 1, so the hexadecimal equivalent of 4096 is 1000.

Therefore, the decimal number 4096 is equivalent to the hexadecimal number 1000.

Final Thoughts


Converting decimal to hexadecimal may seem complicated at first, but it’s actually quite simple once you understand the process. Just remember to divide the decimal number by 16, convert the remainder to hexadecimal, and repeat the process until the quotient is 0. By following these steps, you can easily convert decimal to hexadecimal for any number.

Want to convert Decimal to Hexadecimal Even Faster?


Hex Converter is an application for macOS that can instantly convert decimal numbers to hexadecimal (and vice versa); get Hex Converter on the Mac App Store at a very low price here!

Easily Bind an NSProgress Object to an NSProgressIndicator in Objective-C [Open Source]

In UIKit UIProgressView has an observedProgress property. If you set the observedProgress property on a UIProgressView, it will automatically update its appearance when you make changes to the NSProgress object. On macOS (in AppKit at least) NSProgressIndicator does not have an equivalent API. I created a simple category on NSProgressIndicator that adds an observedProgress property on NSProgressIndicator. The source code is available on Github here.

Adding Force Touch Features to macOS Apps with a Custom Gesture Recognizer [Open Source]

I created a simple subclass of NSGestureRecognizer, ATForceTouchGesture, to simplify the process of adding Force Touch features to macOS apps. You can use this gesture recognizer to add a feature like force clicking to start editing a label (see the screenshot below).

Image captures 'force click' to edit using ATForceTouchGesture.

There is a sample project available on Github here.

Exporting NSTableView to HTML [Open Source]

I needed to export the contents of a NSTableView to HTML, so I wrote a little NSTableView subclass in Objective-C to do this.

ATHyperTextTableView is a simple NSTableView subclass that makes exporting a table view to HTML easy. You can customize the look of the exported HTML table with your own CSS too.

Screenshots Below:
Screenshot of NSTableView.
Screenshot of exported HTML from a the tableView, loaded into a WebView.
Screenshot of the exported HTML loaded into a WebView.

Screenshot of exported HTML from a the tableView, loaded into a WebView with custom CSS set.
Screenshot of the exported HTML loaded into a WebView, styled with custom CSS.

There is a sample project available on Github here.

How to Disable NSScrollView Scrolling

Unlike UIScrollView on iOS, NSScrollView on Mac does not have a handy scrollEnabled property you can use to temporarily disable scrolling.

If you Google around, you can find several posts on websites like stackoverflow that ask questions like How can I disable the vertical scrolling of a NSScrollView? Some have suggested setting the hasVerticalScroller property to NO as the answer. However, the hasVerticalScroller property only effects the visibility of the scroller, a value of NO does not actually prevent scrolling.

There are also ways to constrain scrolling from an NSView subclass, as described in Apple’s documentation here. Constraining scrolling from a view subclass can be useful, but what if you want to just temporarily disable scrolling?

The easiest way to disable scrolling is to subclass NSScrollView and add a BOOL property with a name like scrollingEnabled. Then you can simply override the designated initializers (initWithFrame: and initWithCoder:) and set the ivar of the property to YES as the default value:

-(instancetype)initWithFrame:(NSRect)frameRect 
{ 
   self = [super initWithFrame:frameRect]; 
   if (self) 
   { 
       [self setUpOnInit]; 
   } 
   return self; 
}
 
- (instancetype)initWithCoder:(NSCoder *)coder 
{ 
    self = [super initWithCoder:coder]; 
    if (self) 
    { 
         [self setUpOnInit]; 
    } 
    return self; 
}
 
-(void)setUpOnInit
{
   // Set all default values. 
   _scrollingEnabled = YES; 
}

Now you can override the scrollWheel: method and check the property:

-(void)scrollWheel:(NSEvent *)theEvent 
{ 
    if (self.scrollingEnabled) {  
      [super scrollWheel:theEvent];  
     } 
    else {  
       // scrolling is disabled. 
    } 
}

This technique will work in most cases, but it isn’t enough to completely disable scrolling. If there is a view inside of a scroll view that implements autoscrolling behavior (if the view is a dragging destination) the scrollWheel: override won’t block scrolling during an attempted drag and drop operation. To disable scrolling in all cases, you should also subclass NSClipView and block scrolling by overriding the -constrainBoundsRect: method like this:

-(NSRect)constrainBoundsRect:(NSRect)proposedBounds
{
    MyScrollViewSubclass *mainScrollView = (MyScrollViewSubclass*)self.superview;
 
    if (mainScrollView.scrollingEnabled)
    {
        return [super constrainBoundsRect:proposedBounds];
    }
    else
    {
        // Disabled
        return self.documentVisibleRect;
    }
}

After you do that, scrolling should be disabled whenever you set the scrollingEnabled property to NO. But there may be cases where the scroll bar remains visible even when you have scrolling disabled (depending on what type of mouse is being used). To deal with this you should also make sure you set the hasVerticalScroller property to NO when you disable scrolling. If desired, you can implement the setter of the scrollingEnabled property and set hasVerticalScroller at the same time.