Обратная совместимость нового NSSearchToolbarItem

В macOS 11 Apple представила новый NSToolbarItem под названием NSSearchToolbarItem, который автоматически изменяет размер, чтобы приспособиться к вводу текста, когда фокус переключается на элемент панели инструментов.

введите здесь описание изображения введите здесь описание изображения

Здесь Apple говорит, что это обратно совместимо со старыми версиями macOS: https://developer.apple.com/wwdc20/10104 (11:50 минут)

Однако запуск моего приложения с помощью NSSearchToolbarItem из построителя интерфейса в macOS 10.13 (High Sierra) приводит к сбою моего приложения со следующей информацией о приложении:

*** Завершение работы приложения из-за необработанного исключения 'NSInvalidUnarchiveOperationException', причина: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: невозможно декодировать объект класса (NSSearchToolbarItem) для ключа (NS.objects); класс может быть определен в исходном коде или в несвязанной библиотеке, что завершается необработанным исключением типа NSException.

Запуск 10.15 работает нормально. Я еще не смог протестировать 10.14.

Обновление 6 от 21 июля, автор Томас Темпельманн

Оказывается, это была ошибка в более старых версиях Xcode 12, и теперь она исправлена ​​в Xcode 12.5.1.

Я открыл награду, потому что у меня была, казалось бы, связанная проблема с неправильным размером NSSegmentedControls внутри панели инструментов при работе в High Sierra, но оказалось, что это отдельная проблема (которую можно исправить, вручную сбросив minSize и maxSize в элемент управления frame.size).

Поэтому решение состоит в том, чтобы использовать Xcode 12.5.1 или более позднюю версию.


person Daniel    schedule 08.11.2020    source источник
comment
image lookup -rn NSSearchToolbarItem показывает, что NSSearchToolbarItem существует в AppKit 10.15, но не в 10.13.   -  person Willeke    schedule 09.11.2020
comment
Да, но Apple говорит, что в сеансе 10104 он обратно совместим, если вы загружаете его из файла пера. Его следует заменить на NSToolbarItem на NSSearchField. Но очевидно, что это не так.   -  person Daniel    schedule 09.11.2020
comment
Кстати, о проблеме было сообщено как об ошибке (FB8889904), см. developer.apple.com/forums/ поток/666033   -  person Thomas Tempelmann    schedule 28.06.2021


Ответы (2)


Добавление элемента в раскадровку без кода работает правильно, я только что проверил. Так что, вероятно, вы сделали что-то не так в коде. Или это исправлено в последней версии XCode.

На данный момент я обнаружил, что это работает только на Каталине, даже на Мохаве происходит сбой. По словам @ThomasTempelmann, в XCode 12.5.1 лучше, но я еще не проверял это.

person Ivan Ičin    schedule 26.12.2020
comment
@ThomasTempelmann спасибо за информацию, я обновил информацию, которая у меня есть в настоящее время (в Мохаве, похоже, это не работает для меня). - person Ivan Ičin; 29.06.2021
comment
Что ж, у меня есть более полный ответ, но я не могу присудить награду себе. Наслаждаться! :) - person Thomas Tempelmann; 06.07.2021
comment
@ThomasTempelmann, спасибо!!! - person Ivan Ičin; 06.07.2021

There are two ways to solve this:

1. Use Xcode 12.5.1 or later

Создайте приложение с помощью Xcode 12.5.1 или более поздней версии, которая, по-видимому, исправила совместимость с системами до 10.14.

2. Add the NSSearchToolbarItem in code

Если вы хотите по-прежнему иметь возможность открывать проект с более старыми версиями Xcode (например, Xcode 11 и более ранними), вы не можете поместить новый NSSearchToolbarItem в раскадровку, иначе более старые версии Xcode откажутся открывать его.

В этом случае вы бы продолжали использовать классический NSToolbarItem с элементом управления NSSearchField внутри него. Задача состоит в том, чтобы заменить его на NSSearchToolbarItem при работе с macOS 11 или более поздней версии.

Я пробовал несколько методов, таких как явное удаление элемента классической панели инструментов поиска с панели инструментов, а затем добавление нового и реализация функции делегата для его предоставления. Хотя это работало, это вызывало проблемы, когда пользователь позволял настраивать панель инструментов: тогда диалоговое окно продолжало показывать старый элемент поиска вместе с новым. Единственным способом решить эту проблему был доступ к закрытым функциям (_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. Таким образом, нет необходимости копировать и свойства в коде.

Fixing Segmented Controls

И если у вас есть сегментированные элементы управления на панели инструментов, вам также понадобится этот код для настройки их размеров в зависимости от размещения, поскольку они имеют разную ширину в системах 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];
        }
    }
}

Sample code

страница Github

person Thomas Tempelmann    schedule 06.07.2021