There are two ways to solve this:
1. Use Xcode 12.5.1 or later
Build the app with Xcode 12.5.1 or later, wnhich appears to have fixed the compatibility with pre-10.14 systems.
If you want to still be able to open the project with older Xcode versions (ie. Xcode 11 and earlier), you cannot place the new NSSearchToolbarItem into the storyboard, or older Xcode versions will refuse to open it.
In this case, you'd keep using the classic NSToolbarItem
with an NSSearchField
control inside it. The challenge is to replace it with an NSSearchToolbarItem
when running macOS 11 or later.
I've tried several methods, such as explicitly removing the classic search toolbar item from the toolbar and then adding the new one, and implementing the delegate function to provide it. While that worked, it caused trouble when letting the user customize the toolbar: Then the dialog would keep showing the old search item along with the new one. The only way to resolve this was to access private functions (_setAllowedItems
and _setDefaultItems
), but I wasn't happy with that.
I finally found this fairly solution:
- Create a new custom class, let's name it
SmartSearchToolbarItem
, and make it a subclass of NSToolbarItem
.
- In the storyboard, change the class of the Search Field from
NSToolbarItem
to SmartSearchToolbarItem
.
- Add the following code to the
SmartSearchToolbarItem
implementation:
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101600
@interface NSSearchToolbarItem : NSObject
- (instancetype)initWithItemIdentifier:(NSToolbarItemIdentifier)itemIdentifier;
@end
#endif
@implementation SmartSearchToolbarItem
-(instancetype)initWithItemIdentifier:(NSToolbarItemIdentifier)itemIdentifier
{
self = [super initWithItemIdentifier:itemIdentifier]; // this is necessary even if we won't use it, or we'll crash in Big Sur
Class cls = NSClassFromString(@"NSSearchToolbarItem");
if (cls) {
self = (id) [[cls alloc] initWithItemIdentifier:itemIdentifier];
}
return self;
}
Not only will this automagically replace the classic search item with the new one in Big Sur and later, it will even – and that's the part I don't really understand – still work with connected IBActions and IBOutlets. So, there's no need to copy and properties in code.
Fixing Segmented Controls
And if you happen to have segmented controls in your toolbar, then you'll also need this code to adjust their sizes as placement, as they have different widths on Big Sur vs. earlier macOS systems (10.15 and 10.14 will be fine, but if you also support 10.13, you will need this definitely):
- (void)fixSegmentedToolbarItemWidths // call this from `viewWillAppear`
{
if (@available(macOS 10.14, *)) {
// no need to set the sizes here
} else {
BOOL didChange = NO;
for (NSToolbarItem *item in self.view.window.toolbar.items) {
NSControl *control = (NSControl*)item.view;
if ([control isKindOfClass:NSSegmentedControl.class]) {
[control sizeToFit];
NSRect frame = control.frame;
const int padding = 2;
item.minSize = NSMakeSize(frame.size.width+padding, item.minSize.height);
item.maxSize = item.minSize;
didChange = YES;
}
}
if (didChange) {
[self.view.window.toolbar validateVisibleItems];
}
}
}
Sample code
Github page