How to create a NSTextFieldCell, that in edit mode

2019-04-14 20:32发布

I have a tableView, with 3 columns containing NSTextFieldCell. Everything is populated with bindings.

The text of the cells of one of the column is computed and is not editable directly. For this column, I would like when the cell goes into edit mode, to display a button or a custom view instead of the textField.
Said in an other way, I want an NSCell that is a NSTextFieldCell when not being edited, and a NSButtonCell (or a custom view) when being edited.

Any hint to move on to this?


Here is what I tried, without success:

  • Try #1:

I subclassed a NSTextFieldCell and override fieldEditorForView: like shown below => problem is the NSButton I'm returning does not respond to setDelegate: and probably many other stuff.

- (NSTextView *)fieldEditorForView:(NSView *)aControlView {

    NSTableView *tableView = (NSTableView *) aControlView;

    // Manually computing column and row index for testing purposes
    NSButton* button = [[NSButton alloc] initWithFrame:[tableView frameOfCellAtColumn:2 row:2]];

    return (NSTextView *)button;
}
  • Try #2:

Subclass NSTextFieldCell and override drawWithFrame: inView: => this method is only used to draw the cell when not in edit mode.

  • Try #3:

This lead has some potential, but I'm obviously missing something here.
Implement windowWillReturnFieldEditor: toObject: in my window's delegate, and return a custom field editor. I seem to be on the right track, but I missed something. The argument anObject is never my custom cell (I double checked and it's defined properly in the XIB).

- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)anObject
{
    if ([anObject isKindOfClass:[NSCellTextAndButtonAtEditTime class]])
    {
        NSLog(@"Going there");

        NSButton* button = [[NSButton alloc] initWithFrame:CGRectMake(0, 0, 300, 20)];
        button.title = @"test";

        return button;
    }
    return nil;
}

1条回答
祖国的老花朵
2楼-- · 2019-04-14 21:12

It seems to me the best way to do this is to subclass NSTextView, making a new custom field editor for your custom cell, and then override - (NSTextView *)fieldEditorForView:(NSView *)aControlView in your custom cell to vend it. It looks like the expectation that an NSTextFieldCell's editor be an NSTextView is pretty much mandatory, which is probably why your attempt to return a button failed. I cooked up this brief example which displays a button, and when you press the button, it updates the value of the text field cell to the current time and ends editing. Maybe it'll be helpful:

@interface SOMyEditor : NSTextView
@end

@interface SOMyEditor ()
- (void)p_buttonPressed: (id)sender;
@end

@implementation SOMyEditor

- (id)initWithFrame:(NSRect)frameRect textContainer:(NSTextContainer *)container;
{
    if (self = [super initWithFrame: frameRect textContainer:container])
    {
        NSButton* button = [[[NSButton alloc] initWithFrame: NSMakeRect(0,0,frameRect.size.width, frameRect.size.height)] autorelease];
        button.title = @"Update Time!";
        button.target = self;
        button.action = @selector(p_buttonPressed:);
        button.autoresizingMask |= NSViewWidthSizable | NSViewHeightSizable;
        self.autoresizesSubviews = YES;
        [self addSubview: button];
    }
    return self;
}

- (void)p_buttonPressed: (id)sender
{
    NSString* string = [[NSDate date] description];
    [self insertText: string];
    [self didChangeText];
    [self.window endEditingFor: self];
}

@end

Then have your custom cell class do something like this:

@interface SOMyCustomTextFieldCell : NSTextFieldCell
@end

@interface SOMyCustomTextFieldCell ()
@property (retain) NSTextView* fieldEditor;
@end

@implementation SOMyCustomTextFieldCell
@synthesize fieldEditor = _fieldEditor;

- (NSTextView *)fieldEditorForView:(NSView *)aControlView
{
    if (nil == _fieldEditor)
    {
        _fieldEditor = [[SOMyEditor alloc] init];
        _fieldEditor.fieldEditor = YES;
    }

    return self.fieldEditor;    
}

- (void)dealloc
{
    [_fieldEditor release];
    [super dealloc];
}

@end

There are a couple of tricks here. First, as mentioned, as long as the cell is an NSTextFieldCell, the editor will have to be a subclass of NSTextView. Subclassing NSTextView brings with it a LOT of boilerplate behavior (you know, all the behaviors you normally want and get for free from using NSTextFieldCell for editing). If you want your app to NOT behave as if there's a text editor, you're going to end up doing some work to disable the normal behaviors of NSTextView.

Secondly, when you override - (NSTextView *)fieldEditorForView:(NSView *)aControlView it's important that it return the same field editor for the same cell across multiple calls. If you just create a new NSTextView on every call to that method, it will not work right.

Hope this helps!

查看更多
登录 后发表回答