新NSSearchToolbarItem的向后兼容性

问题描述

在macOS 11上,Apple引入了一个名为NSSearchToolbarItem的新NSToolbarItem,当焦点切换到工具栏项目时,该工具会自动调整大小以适应键入。

enter image description here

enter image description here

苹果在这里说这与旧版本的macOS向后兼容:https://developer.apple.com/wwdc20/10104(11:50分钟)

但是,从macOS 13的界面生成器中使用NSSearchToolbarItem启动我的应用程序时,使用以下特定于应用程序的信息会使我的应用程序崩溃:

***由于未捕获的异常'NSInvalidUnarchiveOperationException'而终止应用程序,原因:'*** -[NSKeyedUnarchiver encodeObjectForKey:]:无法为密钥(NS.objects)解码类(NSSearchToolbarItem)的对象;上课可能是 在源代码中定义或未链接的库终止 带有NSException类型的未捕获异常

在10.15上启动它可以正常工作。我还无法测试10.14。

有什么想法吗?

解决方法

在没有任何代码的情况下在故事板中添加项目可以正常工作,我刚刚测试过。所以可能你在代码中做错了什么。或者在最新的 XCode 中修复。

到目前为止,我发现这仅适用于 Catalina,即使在 Mojave 上也会崩溃。根据@ThomasTempelmann 的说法,它在 XCode 12.5.1 中更好,但我还没有测试过。

,

有两种方法可以解决这个问题:

1。使用 Xcode 12.5.1 或更高版本

使用 Xcode 12.5.1 或更高版本构建应用程序,这似乎已经修复了与 10.14 之前系统的兼容性。

2.在代码中添加 NSSearchToolbarItem

如果您仍然希望能够使用较旧的 Xcode 版本(即 Xcode 11 及更早版本)打开项目,则不能将新的 NSSearchToolbarItem 放入故事板,否则较旧的 Xcode 版本将拒绝打开它。

在这种情况下,您将继续使用带有 NSToolbarItem 控件的经典 NSSearchField。挑战在于在运行 macOS 11 或更高版本时将其替换为 NSSearchToolbarItem

我尝试了几种方法,例如从工具栏中显式删除经典搜索工具栏项,然后添加新项,并实现委托功能来提供它。虽然这样做有效,但在让用户自定义工具栏时会出现问题:然后对话框将继续显示旧的搜索项和新的搜索项。解决此问题的唯一方法是访问私有函数(_setAllowedItems_setDefaultItems),但我对此并不满意。

我终于找到了这个相当的解决方案:

  1. 创建一个新的自定义类,将其命名为 SmartSearchToolbarItem,并使其成为 NSToolbarItem 的子类。
  2. 在故事板中,将搜索字段的类从 NSToolbarItem 更改为 SmartSearchToolbarItem
  3. 将以下代码添加到 SmartSearchToolbarItem 实现中:
#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;
}

这不仅会自动用 Big Sur 中的新搜索项自动替换经典搜索项,而且以后甚至 - 这是我不太了解的部分 - 仍然可以与连接的 IBActions 和 IBOutlets 一起使用。因此,无需在代码中复制和属性。

修复分段控件

如果您的工具栏中碰巧有分段控件,那么您还需要此代码来调整它们的大小作为放置,因为它们在 Big Sur 与早期的 macOS 系统上具有不同的宽度(10.15 和 10.14 会很好) ,但如果你也支持 10.13,你肯定需要这个):

- (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];
        }
    }
}

示例代码

Github page