Getting duplicate header button cell in NSTableView when using NSPopUpButtonCell

一曲冷凌霜 提交于 2019-12-21 02:54:48

问题


I have a dynamic NSTableView which can add a number of columns depending on the data provided. For each column I have set the header cell to be a NSPopUpButtonCell. (Side-note: I've had to use a custom subclass class for NSTableHeaderView otherwise the menu doesn't pop-up). All works well, apart from a duplicate or extra header button cell on the top right. It mirrors perfectly the previous column selection as shown in screenshots. My question is how do I stop the NSTableView from recycling the previous popup header cell? (By the way I have tried the setCornerView method but that only effects the header area above the vertical scrollbar.)


回答1:


I came across the same problem this week. I went with the quick fix,

[_tableView sizeLastColumnToFit];

(However, after discussion with OP this requires that you use a subclass of NSPopUpButtonCell in the header and also NSTableHeaderView. I attach my solution below)

You can to this by combining the approaches outlined here,

  1. PopUpTableHeaderCell
  2. DataTableHeaderView

Here is a simplified snippet,

// 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

Now for the NSTableViewHeader subclass.

//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

You can tie everything together in the AppDelegate -awakeFromNib or similar,

-(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];

}

Other than that I haven't figured out how to properly correct the drawing in that region.

It seems like it's the image of the last cell that is being duplicated. So I slightly more hack-ish approach would be to add a extra column to your table view with a blank name and which intentionally ignores the mouse clicks. Hopefully by setting the display properties of the last column you can make it look the way you want.

I couldn't find any NSTableView or NSTableViewDelegate method that allow control of this region, so may any other solution would be very complicated. I would be interested in a nice solution too, but I hope this gets you started!




回答2:


I have this issue and i don't use NSPopUpButtonCell at all. I just want to tell about other method how to hide this odd header. This methods will not remove an odd table column, i.e. if you have 2 'legal' columns and hide this extra 3rd column header, you will still be able to move separator between 2nd and 3rd column. But in this case you won't see redundant header even if you want to resize any column. I still need solution how to completely remove the redundant column, and why this is happening. (and why Apple won't fix this bug?)

So... you can just calculate index of column which this header belongs to and according to this draw your header or don't. First, subclass NSTableHeaderCell and set it as a cell class for columns. Let assume your subclass named 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
}

Then in TableHeaderCell's drawWithFrame method you should have:

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)
}

After this you won't have redundant header drawn because it not belongs to any column and columnAtPoint method will return -1.



来源:https://stackoverflow.com/questions/21233743/getting-duplicate-header-button-cell-in-nstableview-when-using-nspopupbuttoncell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!