NSOutlineView 更改展开图标

4
在我的大纲视图中,我正在添加自定义单元格。为了绘制自定义单元格,我参考了Cocoa文档中的示例代码。

http://www.martinkahr.com/2007/05/04/nscell-image-and-text-sample/

我想更改单元格的披露图像为自定义图像,我尝试了以下方法。
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item 
    {
        if([item isKindOfClass:[NSValue class]])
        {
            MyData *pDt = (MyData *)[item pointerValue];
            if(pDt->isGroupElement())
            {
                [cell setImage:pGroupImage];
            }
        }
}

但这也不起作用,是否有其他方法可以更改披露图像,另外我如何在willDisplayCell中找出项目是展开还是折叠,以便我可以相应地设置图像。 这是唯一更改披露图像的地方吗?

由于某种原因,我的outline:willDisplayOutlineCell....从未被调用。有任何建议吗? - Tony
完整的自定义示例请查看 https://github.com/johndpope/TreeTest。 - johndpope
5个回答

5

经过我自己的研究和尝试一些答案,我发现其他提到的方法可以解决问题,但需要更多手动干预才能避免屏幕伪影和其他奇怪的行为。

我发现最简单的解决方案如下,在大多数情况下都有效。

这个解决方案还有一个额外的好处,系统会自动处理许多其他情况,例如列移动等,无需您的参与。

- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell
     forTableColumn:(NSTableColumn *)tableColumn
               item:(id)item
{
    [cell setImage:[NSImage imageNamed: @"Navigation right 16x16 vWhite_tx"]];
    [cell setAlternateImage:[NSImage imageNamed: @"Navigation down 16x16 vWhite_tx"]];
}

在您的情况下,您需要将这个与您的类检测逻辑结合起来,并根据您的情况适当地设置单元格图像。


3

一种很好的改变披露图像的方法是使用基于视图的大纲视图:

在带有NSOutlineViewDelegate的ViewController中:

- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    CustomNSTableCellView *cell    = [outlineView makeViewWithIdentifier:tableColumn.identifier owner:self];
    cell.item                      = item;

    return cell;
}

您需要子类化您的NSOutlineView并覆盖该方法:
您需要为您的NSOutlineView创建一个子类并重写以下方法:
- (id)makeViewWithIdentifier:(NSString *)identifier owner:(id)owner
{
    id view = [super makeViewWithIdentifier:identifier owner:owner];

    if ([identifier isEqualToString:NSOutlineViewDisclosureButtonKey])
    {
        // Do your customization
        // return disclosure button view

        [view setImage:[NSImage imageNamed:@"Disclosure_Categories_Plus"]];
        [view setAlternateImage:[NSImage imageNamed:@"Disclosure_Categories_Minus"]];
        [view setBordered:NO];
        [view setTitle:@""];

        return view;
    }

    return view;
}

//Frame of the disclosure view
- (NSRect)frameOfOutlineCellAtRow:(NSInteger)row
{
    NSRect frame = NSMakeRect(4, (row * 22), 19, 19);
    return frame;
}

2

如果您正在寻找Swift2的解决方案,请按以下方式重写您的outlineview的NSRow子类并覆盖didAddSubview方法。

override func didAddSubview(subview: NSView) {
    super.didAddSubview(subview)
    if let sv = subview as? NSButton {
        sv.image = NSImage(named:"IconNameForCollapsedState")
        sv.alternateImage = NSImage(named:"IconNameForExpandedState")
    }
}

1
我已经找了好几个小时了,谢谢! - Clifton Labrum

1

你已经有了基本的想法,但你需要自己绘制图像。这是我使用的代码:

- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
    NSString *theImageName;
    NSInteger theCellValue = [cell integerValue];
    if (theCellValue==1) {
        theImageName = @"PMOutlineCellOn";
    } else if (theCellValue==0) {
        theImageName = @"PMOutlineCellOff";
    } else {
        theImageName = @"PMOutlineCellMixed";
    }

    NSImage *theImage = [NSImage imageNamed: theImageName];
    NSRect theFrame = [outlineView frameOfOutlineCellAtRow:[outlineView rowForItem: item]];
    theFrame.origin.y = theFrame.origin.y +17;
    // adjust theFrame here to position your image
    [theImage compositeToPoint: theFrame.origin operation:NSCompositeSourceOver];
    [cell setImagePosition: NSNoImage];
}

你需要三张不同的图片,如你所见,一张用于ON状态,一张用于OFF状态,还有一张用于MIXED状态,该状态应该处于两者之间。混合状态确保您仍然可以获得开启和关闭动画。

我已经按照您建议的方式进行了操作,但是遇到了一些问题。无论项目是展开还是折叠,thecellvalue始终等于1,并且此代码正在披露图像上绘制图像,因此如果我的图像是透明的,则它也将显示披露图像。 - Amitg2k12
theCellValue 应该不会总是等于 1,对我来说它似乎工作正常。图片也不应该显示,这就是 [cell setImagePosition: NSNoImage]; 的作用,而且在我的情况下也能正常工作。我不确定为什么会发生这种情况(除非你对单元格进行了其他操作),所以我无法提供进一步的帮助。抱歉。:( - Joshua
谢谢回复,实际上我正在使用ImageTextCell,它是从NSTextFieldCell子类化的,并且它会抛出“unrecognized exception”(未识别的异常)来设置setImagePosition。 可能这就是罪魁祸首,但没关系,因为我已经用另一种方法实现了。 - Amitg2k12
我不喜欢在一个本来不需要绘制任何东西的方法里进行绘制,所以我更喜欢 Scott Allen 的这个答案。 - JWWalker
@JWWalker 我同意,这是几年前的事情,但我同意你的看法。如果可能的话,应该避免在 willDisplayOutlineCell 方法中进行绘制。 - Joshua

0

这是我目前尝试并且有效的部分代码。

/* 因为我们正在展示自己的披露和展开按钮 */

- (NSRect)frameOfOutlineCellAtRow:(NSInteger)row {

    return NSZeroRect;
}
- (NSRect)frameOfCellAtColumn:(NSInteger)column row:(NSInteger)row {
    NSRect superFrame = [super frameOfCellAtColumn:column row:row];

    if ((column == 0) && ([self isGroupItem:[self itemAtRow:row]])) {
        return NSMakeRect(0, superFrame.origin.y, [self bounds].size.width, superFrame.size.height);
    }
    return superFrame;
}

我已经子类化了NSOutlineView类并覆盖了这些方法,
[self isGroupItem]用于检查它是否为组。但是遇到了一个问题,现在看起来需要处理鼠标操作:(,双击组行不会切换。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接