我有一个NSArrayController
动态填充一个包含一堆列的表,其中一列有一个弹出按钮。弹出按钮单元格的内容需要使用NSAttributedString
,因为我需要显示带有下标的科学变量(例如X1降低1)。
将弹出单元格的content values
绑定到NSAttributedString
数组会在UI中产生乱码,因为它只能理解纯NSString
个对象。
弹出按钮的菜单不可绑定(即无法通过绑定动态分配)。
弹出按钮菜单的内容也不能动态绑定。
任何人都可以建议一种方法(至少坚持使用表格内容的其余部分绑定)来动态填充NSPopUpButtonCell
菜单中的NSAttributedString
菜单吗?
答案 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
希望有所帮助。