Разделительный элемент в NSPopupButton с привязками

Содержание NSPopupButton связаны с NSArray струн.

Как мы можем вставить разделитель через привязки?

"-"Струны (как в старые / классические дни) не работают, то есть появляются буквально как"-" пункт меню.

Есть ли готовое решение со стандартными классами Какао и привязками?

Это должно быть тривиальной проблемой, но я не могу найти решение проблемы, которая не включает в себя глупые хаки, такие как создание подклассов NSMenu, NSPopupButton или другие не интуитивные обходные пути.

5 ответов

Решение

Я не мог найти чистый способ динамически добавлять разделители в меню при использовании привязок. Самый простой (и наиболее многократно используемый) способ, который я нашел, - это использовать NSMenuDelegate для динамического обмена NSMenuItems с определенным названием, таким как @"---" с разделителями элементов в menuNeedsUpdate: метод делегата.

Шаг 1. Создайте объект NSO, соответствующий протоколу NSMenuDelegate.

#import <Cocoa/Cocoa.h>

@interface SeparatorMenuDelegate : NSObject <NSMenuDelegate>
@end
@implementation SeparatorMenuDelegate

-(void)menuNeedsUpdate:(NSMenu *)menu {
    NSArray* fakeSeparators = [[menu itemArray] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"title == '---'"]];
    for (NSMenuItem* fakeSep in fakeSeparators) {
        [menu insertItem:[NSMenuItem separatorItem] atIndex:[menu indexOfItem:fakeSep]];
        [menu removeItem:fakeSep];
    }
}

@end

Шаг 2: Связать вещи в Интерфейсном Разработчике.

Перетащите Объект в сцену, которая содержит экземпляр NSPopupButton.Вытащить объект

Установите класс объекта в SeparatorMenuDelegate

Установить класс объекта

Twirl откройте элемент управления NSPopupButton в Outline документа и выберите меню внутри него. Затем установите делегата для меню на SeparatorMenuDelegate объект, который вы втащили ранее.

Установить делегата меню

После этого все элементы в меню с заголовком @"---" будут преобразованы в элементы-разделители.

Если у вас есть несколько экземпляров NSPopupButton в одной и той же сцене, вы можете установить делегат их Меню на один и тот же объект (вам нужен только один SeparatorMenuDelegate за сцену).

ИМХО, самое чистое решение по-прежнему заключается в создании подклассов NSMenu - этот вид настройки - именно то, для чего предназначен подкласс. Следующее решение основано на том, что @matt написал много лет назад на Cocoabuilder, и обновлено, чтобы стать более универсальным, в том числе на High Sierra.

Сначала определите "волшебную строку" для представления элемента-разделителя в вашем коде; сделайте это в заголовочном файле, который будут импортированы всеми затронутыми классами. В этом примере я выбрал "---", но, конечно, это может быть любая строка, которая вам нравится:

#define MY_MENU_SEPARATOR @"---"

Во-вторых, создайте подкласс NSMenu и перезапишите два метода, которые добавляют пункты меню, для обработки специального случая разделителя:

@implementation MyMenu

- (NSMenuItem*)addItemWithTitle:(NSString*)aString action:(SEL)aSelector keyEquivalent:(NSString*)keyEquiv
    {
        if ([aString isEqualToString:MY_MENU_SEPARATOR])
        {
            NSMenuItem *separator = [NSMenuItem separatorItem];
            [self addItem:separator];
            return separator;
        }
        return [super addItemWithTitle:aString action:aSelector keyEquivalent:keyEquiv];
    }


- (NSMenuItem*)insertItemWithTitle:(NSString*)aString action:(SEL)aSelector keyEquivalent:(NSString*)keyEquiv atIndex:(NSInteger)index
    {
        if ([aString isEqualToString:MY_MENU_SEPARATOR])
        {
            NSMenuItem *separator = [NSMenuItem separatorItem];
            [self insertItem:separator atIndex:index];
            return separator;
        }
        return [super insertItemWithTitle:aString action:aSelector keyEquivalent:keyEquiv atIndex:index];
    }   

@end

И это все. Задайте для затронутых меню класс MyMenu в Инспекторе удостоверений Интерфейсного Разработчика, и они будут вставлять элементы разделителя, где это необходимо. Работает как для меню, так и для всплывающих окон.

Вот ответ n.Drake в Swift 3.1:

class MySeparatorMenuDelegate : NSObject, NSMenuDelegate {
    func menuNeedsUpdate(_ menu: NSMenu) {
        for (ix,mi) in menu.items.enumerated() {
            if mi.title == "---" {
                menu.removeItem(at: ix)
                menu.insertItem(NSMenuItem.separator(), at: ix)
            }
        }
    }
}

Посмотрите на документацию для NSContentPlacementTagBindingOption, добавлено в Mac OS X 10.5. В инспекторе привязок Интерфейсного Разработчика это доступно для таких элементов, как кнопки всплывающего меню; перейдите в раздел "Выбор значений" и найдите в любой категории контента (контент, объекты контента, значения контента) "Тег размещения контента". Значением поля должно быть число, соответствующее номеру тега какого-либо пункта меню.

Содержимое связанного массива будет вставлено в меню вместо того элемента, который имеет указанное значение тега. В этом случае статическое меню будет содержать ваш разделитель и, по крайней мере, еще один элемент, чтобы указать, куда идут значения массива.

[[popupButton menu] addItem:[NSMenuItem separatorItem]];

Другие вопросы по тегам