【问题标题】:Getting duplicate header button cell in NSTableView when using NSPopUpButtonCell使用 NSPopUpButtonCell 时在 NSTableView 中获取重复的标题按钮单元格
【发布时间】:2014-01-20 12:10:15
【问题描述】:

我有一个动态 NSTableView,它可以根据提供的数据添加许多列。对于每一列,我将标题单元格设置为 NSPopUpButtonCell。 (旁注:我必须为 NSTableHeaderView 使用自定义子类,否则菜单不会弹出)。一切都很好,除了右上角的重复或额外的标题按钮单元格。它完美地反映了之前的列选择,如屏幕截图所示。我的问题是如何阻止 NSTableView 回收以前的弹出标题单元格? (顺便说一句,我尝试了 setCornerView 方法,但这只影响垂直滚动条上方的标题区域。)

【问题讨论】:

    标签: macos cocoa nstableview nspopupbuttoncell


    【解决方案1】:

    这周我遇到了同样的问题。我选择了快速修复,

    [_tableView sizeLastColumnToFit];
    

    (但是,在与 OP 讨论后,这要求您在标题中使用 NSPopUpButtonCell 的子类以及 NSTableHeaderView。我在下面附上我的解决方案)

    您可以通过结合此处列出的方法来实现这一点,

    1. PopUpTableHeaderCell
    2. DataTableHeaderView

    这是一个简化的sn-p,

    // PopUpTableHeaderCell.h
    #import <Cocoa/Cocoa.h>
    /* Credit: http://www.cocoabuilder.com/archive/cocoa/133285-placing-controls-inside-table-header-view-solution.html#133285 */
    
    @interface PopUpTableHeaderCell : NSPopUpButtonCell
    @property (strong) NSTableHeaderCell *tableHeaderCell; // Just used for drawing the background
    
    @end
    
    // PopUpTableHeaderCell.m
    @implementation PopUpTableHeaderCell
    
    - (id)init {
        if (self = [super init]){
    
            // Init our table header cell and set a blank title, ready for drawing
            _tableHeaderCell = [[NSTableHeaderCell alloc] init];
            [_tableHeaderCell setTitle:@""];
    
            // Set up the popup cell attributes
            [self setControlSize:NSMiniControlSize];
            [self setArrowPosition:NSPopUpNoArrow];
            [self setBordered:NO];
            [self setBezeled:NO];
            [self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
        }
        return self;
    }
    
    // We do all drawing ourselves to make our popup cell look like a header cell
    - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView{
    
        [_tableHeaderCell drawWithFrame:cellFrame inView:controlView];
    
        // Now draw the text and image over the top
        [self drawInteriorWithFrame:cellFrame inView:controlView];
    }
    
    @end
    

    现在是 NSTableViewHeader 子类。

    //DataTableHeaderView.h
    #import <Cocoa/Cocoa.h>
    
    /* Credit: http://forums.macnn.com/79/developer-center/304072/problem-of-nspopupbuttoncell-within-nstableheaderview/ */
    
    @interface DataTableHeaderView : NSTableHeaderView
    @end
    
    //DataTableHeaderView.m
    #import "DataTableHeaderView.h"
    
    /* Credit: http://forums.macnn.com/79/developer-center/304072/problem-of-nspopupbuttoncell-within-nstableheaderview/ */
    
    @implementation DataTableHeaderView
    
    - (id)initWithFrame:(NSRect)frame {
    
        self = [super initWithFrame:frame];
        if (self) {
            // Initialization code here.
        }
        return self;
    }
    
    - (void)mouseDown:(NSEvent *)theEvent {
    
        // Figure which column, if any, was clicked
        NSPoint clickedPoint = [self convertPoint:theEvent.locationInWindow fromView:nil];
        NSInteger columnIndex = [self columnAtPoint:clickedPoint];
        if (columnIndex < 0) {
            return [super mouseDown:theEvent];
        }
    
        NSRect columnRect = [self headerRectOfColumn:columnIndex];
    
        // I want to preserve column resizing. If you do not, remove this
        if (![self mouse:clickedPoint inRect:NSInsetRect(columnRect, 3, 0)]) {
            return [super mouseDown:theEvent];
        }
    
        // Now, pop the cell's menu
        [[[self.tableView.tableColumns objectAtIndex:columnIndex] headerCell] performClickWithFrame:columnRect inView:self];
        [self setNeedsDisplay:YES];
    }
    
    - (BOOL)isOpaque {
        return NO;
    }
    
    
    - (void)drawRect:(NSRect)dirtyRect {
        [super drawRect:dirtyRect]; 
        // Drawing code here.
    }
    
    @end
    

    您可以在 AppDelegate -awakeFromNib 或类似中将所有内容绑定在一起,

    -(void) awakeFromNib {
    
        /* NB the NSTableHeaderView class is changed to be an DataTableHeaderView in IB! */
    
        NSUInteger numberOfColumnsWanted = 5;
        for (NSUInteger i=0; i<numberOfColumnsWanted; i++) {
    
            PopUpTableHeaderCell *headerCell;
            headerCell = [[PopUpTableHeaderCell alloc] init];
    
            [headerCell addItemWithTitle:@"item 1"];
            [headerCell addItemWithTitle:@"item 2"];
            [headerCell addItemWithTitle:@"item 3"];
    
            NSTableColumn *column;
            [column setHeaderCell:headerCell];
            [column sizeToFit];
    
            [_tableView addTableColumn:column];
        }
    
        /* If we don't do this we get a final (space filling) column with an unclickable (dummy) header */
        [_tableView sizeLastColumnToFit];
    
    }
    

    除此之外,我还没有弄清楚如何正确纠正该区域的绘图。

    这似乎是被复制的最后一个单元格的图像。所以我稍微有点hack-ish的方法是在你的表格视图中添加一个额外的列,名称为空白,并且故意忽略鼠标点击。希望通过设置最后一列的显示属性,您可以使它看起来像您想要的那样。

    我找不到任何允许控制该区域的 NSTableView 或 NSTableViewDelegate 方法,因此任何其他解决方案可能会非常复杂。我也会对一个好的解决方案感兴趣,但我希望这能让你开始!

    【讨论】:

    • 我已将其范围缩小到导致问题的 NSPopUpButtonCell。如果我使用标准的 NSTableHeaderCell,则 sizeLastColumnToFit 方法可以工作,但是当我使用 NSPopUpButtonCell 时,它不会工作。不确定是否有解决方法?
    • 解决问题的详细答案。尽管有一点需要注意:- 默认情况下,用户可以调整列的大小,如果他们向内调整最后一列的大小,他们当然会显示一个重复的标题单元格。为了避免这种情况,我建议设置 [tableView setAllowsColumnResizing:NO];
    • 你找到导致这个错误的原因了吗?
    【解决方案2】:

    我有这个问题,我根本不使用 NSPopUpButtonCell。 我只想告诉其他方法如何隐藏这个奇怪的标题。此方法不会删除奇数表列,即如果您有 2 个“合法”列并隐藏此额外的第 3 列标题,您仍然可以在第 2 列和第 3 列之间移动分隔符。但在这种情况下,即使您想调整任何列的大小,您也不会看到多余的标题。 我仍然需要解决方案如何完全删除冗余列,以及为什么会发生这种情况。 (以及为什么 Apple 不修复这个错误?)

    所以...您可以只计算此标题所属的列的索引,并根据此绘制您的标题或不绘制。首先,子类NSTableHeaderCell 并将其设置为列的单元类。假设您的子类名为TableHeaderCell

    for column in self.tableView.tableColumns {
        let col:NSTableColumn = column as! NSTableColumn
        //you can operate with header cells even for view-based tableView's 
        //although the documentation says otherwise.
        col.headerCell = TableHeaderCell(textCell: col.title) 
        //or what initialiser you will have
    }
    

    那么在 TableHeaderCell 的drawWithFrame 方法中你应该有:

    override func drawWithFrame(cellFrame: NSRect, inView controlView: NSView) {
        let headerView = controlView as! HashTableHeaderView
        let columnIndex = headerView.columnAtPoint(cellFrame.origin)
        if columnIndex == -1 {
            return
        }
    
        //parent's drawWithFrame or your own draw logic:
        super.drawWithFrame(cellFrame, inView: controlView)
    }
    

    在此之后,您将不会绘制多余的标题,因为它不属于任何列,columnAtPoint 方法将返回 -1。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-07
      • 1970-01-01
      • 1970-01-01
      • 2019-02-14
      • 1970-01-01
      • 1970-01-01
      • 2013-11-14
      • 1970-01-01
      相关资源
      最近更新 更多