Search code examples
objective-ccocoacocoa-bindingsnstextfield

Update property bound from text field without needing to press Enter


I have a text field and I bind it to an NSString instance variable.

When I type in the text field, it does not update the variable. It waits until I press the Enter key. I don't want to hit Enter every time.

What do I need to change in order to make the binding change value immediately?


Solution

  • By default, the value binding of an NSTextField does not update continuously. To fix this, you need, after selecting your text field, to check the "Continuously Updates Value" box in the Bindings Inspector under the Value heading:

    NSTextField Value binding with Continuously Updates Value checked.


    However, most often, what you really want to do is update the property to which the text field is bound when the user has finished editing and presses a button ("Save" or "OK", for example). To do this, you needn't continuously update the property as described above, you just need to end editing. Daniel Jalkut provides an extremely useful implementation of just such a method:

    @interface NSWindow (Editing)
    
    - (void)endEditing;
    
    @end
    
    @implementation NSWindow (Editing)
    
    - (void)endEditing
    {
        // Save the current first responder, respecting the fact
        // that it might conceptually be the delegate of the 
        // field editor that is "first responder."
        id oldFirstResponder = [oMainDocumentWindow firstResponder];
        if ((oldFirstResponder != nil) &&
            [oldFirstResponder isKindOfClass:[NSTextView class]] &&
            [(NSTextView*)oldFirstResponder isFieldEditor])
        {   
            // A field editor's delegate is the view we're editing
            oldFirstResponder = [oldFirstResponder delegate];
            if ([oldFirstResponder isKindOfClass:[NSResponder class]] == NO)
            {
                // Eh ... we'd better back off if 
                // this thing isn't a responder at all
                oldFirstResponder = nil;
            }
        } 
    
        // Gracefully end all editing in our window (from Erik Buck).
        // This will cause the user's changes to be committed.
        if([oMainDocumentWindow makeFirstResponder:oMainDocumentWindow])
        {
            // All editing is now ended and delegate messages sent etc.
        }
        else
        {
            // For some reason the text object being edited will
            // not resign first responder status so force an 
            /// end to editing anyway
            [oMainDocumentWindow endEditingFor:nil];
        }
    
        // If we had a first responder before, restore it
        if (oldFirstResponder != nil)
        {
            [oMainDocumentWindow makeFirstResponder:oldFirstResponder];
        }
    }
    
    @end
    

    So if for example you had a "Save" button targeting your view controller's method -save:, you would call

    - (IBAction)save:(id)sender
    {
        [[[self view] window] endEditing];
        //at this point, all properties bound to text fields have the same
        //value as the contents of the text fields.
    
        //save stuff...
    }