Привязка содержимого NSPopupButton к NSAttributedString
У меня есть NSArrayController
динамически заполняет таблицу множеством столбцов, один из которых имеет всплывающую кнопку. Содержимое ячейки всплывающей кнопки необходимо использовать NSAttributedString
так как мне нужно отобразить научную переменную с индексом (например, X1 с пониженным 1).
Привязка всплывающей ячейки content values
к массиву NSAttributedString
приводит к бреду в интерфейсе, так как понимает только NSString
объекты.
Меню всплывающей кнопки не является привязываемым (то есть невозможно назначать динамически через привязки).
Содержимое всплывающего меню кнопок также не может быть связано динамически.
Кто-нибудь может предложить способ (придерживаясь привязок по крайней мере для остальной части содержимого таблицы) для динамического заполнения NSPopUpButtonCell
меню с NSAttributedString
объекты?
1 ответ
Для отображения приписанных строк в меню, когда оно всплывающее, я предлагаю установить столбец таблицы, содержащий всплывающую ячейку, чтобы он Content
обязательное указание на NSArrayController
что само по себе связано с NSArray
из NSAttributedStrings
содержащий все параметры, а затем поставить делегата на NSMenu
содержится во всплывающей ячейке, а затем делает что-то вроде этого в делегате:
- (void)menuNeedsUpdate:(NSMenu*)menu
{
for (NSMenuItem* item in menu.itemArray)
if ([item.representedObject isKindOfClass: [NSAttributedString class]])
{
item.attributedTitle = item.representedObject;
}
}
Привязка поставит не домогаться NSAttributedString
в representedObject
собственность NSMenuItem
, Вы можете найти его там и положить в attributedTitle
свойство, которое заставит его показывать приписанную строку в меню. В итоге, пункт меню, нарисованный в меню, с его attributedTitle
свойство, установленное соответствующим образом, нарисует стилизованный текст.
Что немного сложнее, так это рисование атрибутивной строки, как и предполагалось во всплывающей ячейке, когда меню не отображается. NSPopUpButtonCell
кажется, чтобы сделать, имея NSMenuItem
это привлекает к этому. К сожалению, создание этого конкретного NSMenuItem
Похоже, не включает в себя толкание нематериального значения в нем. Вместо этого заголовок представляется в виде простой строки без атрибутов. Я не смог придумать элегантного решения для этого, но я нашел неуместный обходной путь:
Сначала добавьте NSTextField
колонка к вашему NSTableView
который рисует текущую выбранную приписанную строку правильно (то есть с атрибутами). Сделайте этот столбец скрытым. Подкласс NSPopUpButtonCell
или используйте категорию и соответствующее хранилище, чтобы добавить новое частное свойство в NSPopUpButtonCell
, Это свойство будет содержать блок, который вы можете использовать во время отрисовки для извлечения соответствующей ячейки из скрытого столбца. Добавить NSTableViewDelegate
и реализовать -tableView:dataCellForTableColumn:row:
, Когда это вызывается для всплывающего столбца, создайте блок для извлечения ячейки из скрытого столбца и вставьте ее в свойство вашего подкласса. Затем во время рисования, если у вас есть блок выборки ячеек, очистите title
на menuItem
что он обычно использовал бы для рендеринга, вызывал super (чтобы получить маленькие стрелки для всплывающего окна), затем извлекал суррогатную ячейку и выполнял ее рисование. Вот как выглядит код:
@interface AppDelegate : NSObject <NSApplicationDelegate, NSMenuDelegate, NSTableViewDelegate>
@property (assign) IBOutlet NSTableColumn *popUpColumn;
@property (assign) IBOutlet NSTableColumn *surrogateColumn;
// ...snip...
@end
@interface SOPopUpButtonCell : NSPopUpButtonCell
typedef NSTextFieldCell* (^CellFetcher)();
@property (nonatomic, copy, readwrite) CellFetcher cellFetcherBlock;
@end
@implementation AppDelegate
// ...snip...
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
if (nil == tableColumn || self.popUpColumn != tableColumn)
return nil;
SOPopUpButtonCell* defaultCell = (SOPopUpButtonCell*)[tableColumn dataCellForRow: row];
const NSUInteger columnIndex = [[tableView tableColumns] indexOfObject: self.surrogateColumn];
CellFetcher f = ^{
return (NSTextFieldCell*)[tableView preparedCellAtColumn: columnIndex row: row];
};
defaultCell.cellFetcherBlock = f;
return defaultCell;
}
@end
@implementation SOPopUpButtonCell
- (void)setCellFetcherBlock:(CellFetcher)cellFetcherBlock
{
if (_cellFetcherBlock != cellFetcherBlock)
{
if (_cellFetcherBlock)
Block_release(_cellFetcherBlock);
_cellFetcherBlock = cellFetcherBlock ? Block_copy(cellFetcherBlock) : nil;
}
}
- (void)dealloc
{
if (_cellFetcherBlock)
Block_release(_cellFetcherBlock);
[super dealloc];
}
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
CellFetcher f = self.cellFetcherBlock;
if (f)
self.menuItem.title = @"";
[super drawWithFrame:cellFrame inView:controlView];
if (f)
NSTextFieldCell* surrogateCell = f();
[surrogateCell drawWithFrame: cellFrame inView: controlView];
}
@end
Я должен признать, что это заставляет меня чувствовать себя немного грязным, но похоже, что работа сделана. Я разместил весь код, включая xib со всеми связанными привязками на github: Пример проекта
Надеюсь, это поможет.