1

When a user adds a new managed object, it shows up in a table, which scrolls down to the new entry, and the name of the new object (a default value) goes into editing mode.

I need to check if the name of the new object is unique in the datastore, so I can't use a formatter for this. I think the perfect moment where I should validate this, is whenever the user tries to commit the entry's name value, using textShouldEndEditing:.

I subclassed NSTableView and overrid following methods, just to be able to check in the log if they get called.

- (BOOL)textShouldEndEditing:(NSText *)textObject {
    NSLog(@"textSHOULDendEditing fired in MyTableView");
    return [super textShouldEndEditing:textObject];
}
- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
    NSLog(@"control:textShouldEndEditing fired in MyTableView");
    return YES;
}
- (void)textDidEndEditing:(NSNotification *)aNotification {
    NSLog(@"textDIDEndEditing fired in MyTableView");
}

textDidEndEditing: gets called fine, but textShouldEndEditing: does not.

In the NSTableView Class Reference, under Text Delegate Methods, both methods textShouldEndEditing: and textDidEndEditing: are listed. Someone please explain why one gets called and the other doesn't.


I think the NSTableView acts as the delegate for an NSTextField that gets instantiated as a black box delegate for the NSTextFieldCell. So what is referred to as delegate methods in the NSTableView Class Reference, actually implement the text manipulating methods for the NSTextField object.

I tried to declare the NSTextFieldCell as an outlet in my NSTableView. I also tried to declare several protocols in the NSTableView.

#import <AppKit/AppKit.h>
#import <Cocoa/Cocoa.h>
@interface MyTableView : NSTableView <NSTextDelegate, NSTextFieldDelegate, NSControlTextEditingDelegate, NSTableViewDelegate, NSTableViewDataSource> {
}
@end

Don't laugh, I even tried to declare my table view as its own delegate :P

Fnord23
  • 325
  • 2
  • 18
  • Is it not the `` protocol that needs to be implemented? Does anyone know how `NSTextDelegate` methods of the `NSTextFieldCell` are forwarded? Is there any documentation around? (Believe me, I've looked) Or is there an object up the command chain that functions by default as `NSTextDelegate` (such as the `NSTableView`)? Is it possible that some `NSTextDelegate` methods are overridden by another class which is first in the command chain? – Fnord23 Apr 03 '12 at 13:23
  • Found an interesting document in the Mac OS X Developer Libraries: [Cocoa Text Architecture Guide](http://developer.apple.com/library/mac/#documentation/TextFonts/Conceptual/CocoaTextArchitecture/Introduction/Introduction.html#//apple_ref/doc/uid/TP40009459-CH1-SW1), with lots of pointers to other technologies and examples. I'll get back when I know more :) – Fnord23 Apr 03 '12 at 15:29
  • The [Text Editing](http://developer.apple.com/library/mac/#documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextEditing/TextEditing.html#//apple_ref/doc/uid/TP40009459-CH3-SW1) chapter of the Cocoa Text Architecture Guide says: **Text Change Notifications and Delegate Messages** _The textShouldBeginEditing: and textDidBeginEditing: messages are sent only once during an editing session. More precisely, they’re sent upon the first user input since the NSTextView became the first responder._ The fact that the text was selected did not notify the text field that editing was being done. – Fnord23 Apr 05 '12 at 17:01
  • The view may not be the best place to validate user entry. When the user adds a new managed object by clicking on an Add-button, a `-(IBAction)add:(id)sender` message is sent to the array controller, which in turn uses the entity description to insert a new managed object into the managed object context. So when a user actually gets to edit the name-property of the new managed object, at least some validation should already have happened. When the (non-optional) name-property is not set to have a Default Value (in IB), I'd like to make sure that the name property is not left blank. – Fnord23 Apr 16 '12 at 08:44
  • Funny thing is that when the user clicks the Add-button and hits Enter without making any changes, the subclassed managed object's `-(BOOL)validate:error:` method is not called. On the other hand, the validation does get called when the user actively edits the field by deleting an existing name. – Fnord23 Apr 16 '12 at 08:44

3 Answers3

7

After banging my head one entire day on this issue without finding any conclusive answer in Apple documentation, I decided to share the solution I've found in case somebody else struggles with the same problem.

According to the documentation, as the original poster mentioned, the methods control:textShouldBeginEditing and control:textShouldEndEditing of NSControlTextEditingDelegate should be called directly on the delegate:

This message is sent by the control directly to its delegate object.

Furthermore, a Technical Q&A was issued by Apple with the title Detecting the start and end edit sessions of a cell in NSTableView where it's clearly stated the following:

A: How do I detect start and end edit sessions of a cell in NSTableView?

In order to detect when a user is about to start and end an edit session of a cell in NSTableView, you need to be set as the delegate of that table and implement the following NSControl delegate methods:

- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor;

- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor;

The table forwards the delegate message it is getting from the text view on to your delegate object using the control:textShouldEndEditing: method. This way your delegate can be informed of which control the text view field editor is acting on its behalf.

I found nothing in Apple's documentation stating anything different and if someone does, a documentation pointer would really be appreciated.

In fact, this appears to be true if a cell-based NSTableView is being used. But as soon as you change the table to a view-based table, the delegate method is not called any longer on the table delegate object.

A Solution

However, some heuristic tests I performed showed that those delegate methods get called on a view-based table delegate if (and as far as I know: and only if):

  • The table delegate is set.
  • The delegate of the editable control is set.

If you remove either delegate, the methods of the NSControlTextEditingDelegate protocol will not be called.

What's unexpected according to the (only) documentation is setting the delegate of the editable control. On the other hand setting the delegate object to receive delegate notifications sounds rather intuitive to me, and that's why I tried in the first place. But there's a catch! The curious thing, though, is that that's not sufficient. If the table delegate is removed, the NSControlTextEditingDelegate methods will not be called even if the delegate of the editable control is set (which is the weirdest thing to me).

Hope this helps somebody else not to lose time on this issue.

Enrico M. Crisostomo
  • 1,653
  • 1
  • 17
  • 26
  • 1
    This helped me out. In my case, there was an added complication: I was setting the tableView.delegate in awakeFromNib (though I had bindings set for the tableview in my nib) ... I still wasn't getting the callbacks. After I removed the line in code and set the delegate in nib, it worked. – Z S Nov 22 '14 at 03:06
  • Thank you. Six years later, and I'd still never guess that the NSTableView needed a delegate set, even if no methods of that delegate were implemented, in order for the NSControlTextEditingDelegate methods to be called. Crazy, but it works.. – Jeff Hay Mar 13 '20 at 17:47
2

in your question you mention the insertion of a "managed object" and that was the problem. It seems that you are using a view based table, but the textShouldEndEditing: method only gets called for cell based tables.

SystematicFrank
  • 16,555
  • 7
  • 56
  • 102
1

I overrid -(void)awakeFromInsert; in the (subclassed) managed object, to construct a unique default value for the name-property.

Also, I ended up not overriding the -(BOOL)textShouldEndEditing: method in the table view. Instead, I check if a newly entered name-property is unique in the (subclassed) managed object's -(BOOL)validate<Key>:error:.

Together, the above two strategies result in unique name-properties in all managed objects.

Maybe I could have forced the NSTextFieldCell to go into editing mode, resulting in -(BOOL)textShouldEndEditing: to get called every time.


Some remarks though:

It seems -(BOOL)textShouldEndEditing: returns NO when the -(BOOL)validate<Key>:error: returns NO.

Both -(BOOL)textShouldEndEditing: and -(BOOL)validate<Key>:error: methods are called only when the user actually makes changes to the property.

Fnord23
  • 325
  • 2
  • 18
  • 1
    Belated note for anyone who finds this answer with the same problem I did: when the text field’s contents haven’t changed, you get a `-controlTextDidEndEditing:` rather than `-control:textShouldEndEditing:`. – Noah Witherspoon Jun 02 '17 at 23:20