tags:

views:

403

answers:

4

In a Mac OS X Cocoa application, I have an application-modal dialog with text fields that are bound to the Shared User Defaults Controller. If I edit a text field, and then tab away from it before hitting the OK button, then everything works as desired. However, if I start editing a field, then hit the Return key to trigger OK, the old value of the field remains in NSUserDefaults.

So, how can I force the changed field to affect the bound value when editing is "incomplete"?

From perusing documentation, I think I might be able to call NSControl's validateEditing method for each of the text fields before dismissing the dialog, but it seems like there should be a simpler way.

FWIW, here is the code that displays the dialog:

- (void)showDialog {
    [NSApp activateIgnoringOtherApps:YES];
    [NSApp beginSheet:startTimerDialog
       modalForWindow:nil
        modalDelegate:nil
       didEndSelector:nil
          contextInfo:nil];
    [NSApp runModalForWindow:startTimerDialog];
    [NSApp endSheet:startTimerDialog];
    [startTimerDialog orderOut:self];
}

The OK button (actually titled "Start") is targetted to this method:

- (IBAction)startTimerDialogStartButtonWasClicked:(id)sender {
    [self closeModalDialog:sender];

    // Then, call methods that read values from NSUserDefaults
    // ...    
}
A: 

You could try setting a delegate for all your text fields and have that handle changes directly.

Abizern
Will the delegate get a notification in the case I'm trying to handle?
Kristopher Johnson
+1  A: 

This might be the answer:

[startTimerDialog makeFirstResponder:nil];

According to the docs, this sends resignFirstResponder to whatever is currently the first responder, which should cause the editing to end.

Of course, the first responder can refuse to resign, in which case makeFirstResponder will return NO. So I need to look into how to handle that.

Kristopher Johnson
+4  A: 

Send the user defaults controller a commitEditing message.

Peter Hosey
+1  A: 

A slightly abrupt way to end editing can be achieved with the following call:

[startTimerDialog endEditingFor:nil];

This always works, but it's a bit more intense than you'll need most of the time. (for a more official description of "intense" see the documentation link below).

In short: -endEditingFor: should only be used as a last resort when you can't get the fieldEditor to resign first responder. So you might want to try a more subtle approach before you resort to the extreme measures like this.

To be more subtle, in the NSWindow class documentation for -endEditingFor: Apple proposes something resembling this:

if ( ![startTimerDialog makeFirstResponder:startTimerDialog] )
    /* Making the window firstResponder failed, proceed to
       force the first responder to resign */

    [startTimerDialog endEditingFor:nil];

Follow the above link to see their exact example code, I shortened it here because you're obviously not interested in using the fieldEditor after this in your described use case.

Dirk Stoop