NSPopupButton内容绑定到NSAttributedString

时间:2012-11-17 06:55:45

标签: objective-c cocoa cocoa-bindings nspopupbuttoncell

我有一个NSArrayController动态填充一个包含一堆列的表,其中一列有一个弹出按钮。弹出按钮单元格的内容需要使用NSAttributedString,因为我需要显示带有下标的科学变量(例如X1降低1)。

将弹出单元格的content values绑定到NSAttributedString数组会在UI中产生乱码,因为它只能理解纯NSString个对象。

弹出按钮的菜单不可绑定(即无法通过绑定动态分配)。

弹出按钮菜单的内容也不能动态绑定。

任何人都可以建议一种方法(至少坚持使用表格内容的其余部分绑定)来动态填充NSPopUpButtonCell菜单中的NSAttributedString菜单吗?

1 个答案:

答案 0 :(得分:2)

为了在菜单弹出时在菜单中显示属性字符串,我建议设置包含弹出单元格的表格列,让它的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的{​​{1}}属性中。您可以在那里找到它并将其放入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

我必须承认,这让我觉得有点脏,但似乎完成了工作。我在github上发布了所有代码,包括带有所有相关绑定的xib: Example Project

希望有所帮助。