2
votes

I want to change the value being displayed in a NSTableView Cell while editing it. My requirement is the following: Initially I entered a cell value of 234.5678978 and after editing the value is being rounded up to 0 decimal precision(that means 235). My requirement is that when I could click that cell, it should show me value unto certain decimal precision say up 5 precision, in this case 234.56790). How can I achieve this functionality. I tried to capture the action of double click editing the cell of NSTableView:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(editingDidEnd:)
                                                 name:NSControlTextDidEndEditingNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(editingDidBegin:)
                                                 name:NSControlTextDidBeginEditingNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(editingDidChange:)
                                                 name:NSControlTextDidChangeNotification object:nil];

- (void)editingDidEnd:(NSNotification *)notification
{
    NSLog(@"text editing fisnished");
}

- (void)editingDidBegin:(NSNotification *)notification
{
    NSLog(@"text is being edited");
}
- (void)editingDidChange:(NSNotification *)notification
{
    NSLog(@"text was being changed");
}

But only editingDidEnd was called on tab out and the other two methods were never called.

3

3 Answers

2
votes

I added a custom formatter for the edited cell in willDisplayCell delegate of NSTableView

- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    if(tableView == self.tableView)
    {
        if(![[tableColumn identifier]isEqualToString:@"name"]){
            [cell setFormatter:nil];

            if (row == [tableView editedRow] && [[tableView tableColumns] indexOfObject:tableColumn] == [tableView editedColumn]) // the cell is getting edited
            {
                [(NSTextFieldCell*)cell setTextColor:[NSColor redColor]];
               NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
                [nf setMaximumFractionDigits:5]; // Setting the precision value upto 5 decimal places
                [nf setMinimumFractionDigits:0];
                [cell setFormatter:nf];
            }
            else{
                [(NSTextFieldCell*)cell setTextColor:[NSColor blackColor]];
                //[cell setFormatter:nil];
                NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
                [nf setMaximumFractionDigits:2]; // setting decimal value upto 2 decimal places
                [nf setMinimumFractionDigits:0];
                [cell setFormatter:nf];
            }
        }

    }
}

So, when a cell is edited, it will set the precision to 5 decimal places and in all other cases it will set cell formatter to 2 decimal places.

0
votes

I recommend that you create a custom subclass of NSFormatter. There are two methods, -stringForObjectValue: and -editingStringForObjectValue:. The distinction is precisely what you're looking for. The first provides the value when not editing. The second provides the value when editing, for cases where it needs to be formatted differently.

Because NSNumberFormatter does a lot of nitty-gritty work for you, I recommend that you build your class to use an NSNumberFormatter or two for its implementation. That is, the methods of your class would call into an instance of NSNumberFormatter to do their work. You would either keep two number formatters, one for the not-editing case and the other for the editing case, or you'd use one and keep changing its configuration just before calling it, depending on which method you're implementing.

0
votes

Well, i don't know if i'm getting correctly what you need. It seems you're dealing with a number formatting problem. Anyway, the selectors you mention in your question are invoked by the AppKit but only if something happens in the editor view (i.e. a keystroke, for example). Simply clicking the control (making it firstResponder) will fire nothing. If i were you, actually, i'd rather use delegate methods (https://developer.apple.com/library/mac/qa/qa1551/_index.html but also http://thegreyblog.blogspot.it/2014/06/nscontroltexteditingdelegate-methods.html ) instead of passing through the defaultNotificationCenter. In any case, this is how i would catch very early the "BeginEdit" event, with the possibility of changing whatever value i want in the relevant TextField.

@interface YourTableView : NSTableView

@end


@implementation YourTableView

- (BOOL)validateProposedFirstResponder:(NSResponder *)responder forEvent:(NSEvent *)event {
    BOOL acceptFirstResponder = [super validateProposedFirstResponder:responder forEvent:event];
    if (acceptsFirstResponder && (
        event == nil || // catches tabs
        event.type == NSLeftMouseDown)) { // catches mouse clicks) {
        [responder setStringValue:@"<YourFormattedNumber>"];
    }
    return acceptFirstResponder;
}

- (BOOL)resignFirstResponder {

// Additional formatting code
    return YES;
}

@end

i.e. subclass your tableview and then override -validateProposedFirstResponder