How to use a button to update an application with a text field value

  • I am writing a Cocoa application that contains an NSTextField control.
    Things are such that when a user types a value into this control and
    hits enter, a binding will update an NSController instance in the
    application with the value contained in the text field.

    This is more elegant that having to rely on creating an IBOutlet in the
    controller class and establishing a connection between the outlet and an
    action in the text field control to accomplish the same thing.

    However, the application also contains a button that when clicked is
    also supposed to provide the controller with the contents of the text
    field.  Obviously this could be done be create the appropriate outlets
    and actions and connecting them up in Interface Builder.  However, this
    kind of defeats the whole purpose of using the binding mentioned
    earlier.  It would be desirable to avoid creating outlets and actions
    for the button functionality as  well.

    Perhaps something could be done using the argument and action bindings
    in the button to affect the desired result.  It is not obvious this
    could accomplished.

    One thing I found was that if I implemented an action in my controller
    and connected it to the button, the controller could then call the
    commitEditing method in response to that action.  This could accomplish
    the same thing as pressing enter in the text field control.

    This only works though when the text field has been edited.  If the text
    had not been edited, the user could still expect  the button to cause
    the update.  It would not be clear to them why the button wouldn't do
    anything when no editing had taken place.

    One thought I had to make this relationship clear was to disable the
    button initially and then enable it only after the text field had been
    edited.  I found I could do this by using the objectDidBeginEditing:
    method.  By responding to that method I could use a binding that would
    affect the enabled state of the button.

    In spite of this, what I also find is it that after implementing the
    objectDidBeginEditing: method, the action for the button that calls
    commitEditing no longer works.  For some reason the control thinks that
    no editors need to commit any editing.  If I remove the implementation
    for objectDidBeginEditing: things work fine.  When I add it back in,
    things fail.

    What can help me to implement the functionality I'm trying to develop?
  • Why not just grab the field's -stringValue: after you call
    commitEditing? In other words pull the value from the field instead of
    trying to provoke it into pushing its value somewhere. Much easier, no?

    hth,

    Graham

    On 6 Aug 2008, at 1:45 pm, Tron Thomas wrote:

    > What can help me to implement the functionality I'm trying to develop?
  • The way things are now, when things work right, (i.e. when
    objectDidBeginEditing: is not implemented) the call to commitEditing
    will cause the text field's action to fire which will use the binding
    between the text field value and the NSController's property to cause
    the update to occur with no outlet/action interaction needed.

    For what you are suggesting, I believe the controller would require an
    IBOutlet instance variable that referred to the text field, so it could
    send the stringValue message to that control and get its result.  If I'm
    going to take this approach, I might as well abandon the binding all
    together.  However, the binding works really slick without having to add
    anything extra to the class.

    I'd let to see if an elegant solution that only needs the bindings can
    be found for this problem.

    Graham Cox wrote:
    > Why not just grab the field's -stringValue: after you call
    > commitEditing? In other words pull the value from the field instead of
    > trying to provoke it into pushing its value somewhere. Much easier, no?
    >
    > hth,
    >
    > Graham
    >
    >
    > On 6 Aug 2008, at 1:45 pm, Tron Thomas wrote:
    >
    >> What can help me to implement the functionality I'm trying to develop?
    >
    >
    >
  • On Aug 5, 2008, at 10:45 PM, Tron Thomas wrote:

    > One thing I found was that if I implemented an action in my
    > controller and connected it to the button, the controller could then
    > call the commitEditing method in response to that action.  This
    > could accomplish the same thing as pressing enter in the text field
    > control.

    Yes, that's what -commitEditing is for.

    > This only works though when the text field has been edited.  If the
    > text had not been edited, the user could still expect  the button to
    > cause the update.  It would not be clear to them why the button
    > wouldn't do anything when no editing had taken place.

    I don't understand.  If no editing is in progress, then the value in
    the model and the value displayed in the text field should already be
    in sync.

    What is it that you want to happen when the user presses the button?
    If there's something that needs to happen above and beyond the text
    field's value being pushed to the model, then... just make it happen
    in the action method (after the -commitEditing).

    >
    >
    > One thought I had to make this relationship clear was to disable the
    > button initially and then enable it only after the text field had
    > been edited.  I found I could do this by using the
    > objectDidBeginEditing: method.  By responding to that method I could
    > use a binding that would affect the enabled state of the button.
    >
    > In spite of this, what I also find is it that after implementing the
    > objectDidBeginEditing: method, the action for the button that calls
    > commitEditing no longer works.  For some reason the control thinks
    > that no editors need to commit any editing.  If I remove the
    > implementation for objectDidBeginEditing: things work fine.  When I
    > add it back in, things fail.

    Sounds like your override of -objectDidBeginEditing: is not calling
    through to super.  You're effectively replacing the implementation
    rather than extending it.

    That said, it sounds to me like you're approaching things in a weird
    way.

    Cheers,
    Ken
  • The program is a simple game.  The player answers questions by typing
    something in and then either pressing enter or clicking on the button to
    commit their answer.  Although not likely it is, possible the user may
    try to use the previous information as the answer to the current
    question, so they might simply click the button to resubmit an answer
    without editing anything.

    I am not calling the super method for objectDidBeginEditing.  I thought
    it worked like a delegate method.  Perhaps this is where the problem
    lies.  I can try calling [super objectDidBeginEditing:editor], and see
    how that works.

    Ken Thomases wrote:
    > On Aug 5, 2008, at 10:45 PM, Tron Thomas wrote:
    >
    >> One thing I found was that if I implemented an action in my
    >> controller and connected it to the button, the controller could then
    >> call the commitEditing method in response to that action.  This could
    >> accomplish the same thing as pressing enter in the text field control.
    >
    > Yes, that's what -commitEditing is for.
    >
    >> This only works though when the text field has been edited.  If the
    >> text had not been edited, the user could still expect  the button to
    >> cause the update.  It would not be clear to them why the button
    >> wouldn't do anything when no editing had taken place.
    >
    > I don't understand.  If no editing is in progress, then the value in
    > the model and the value displayed in the text field should already be
    > in sync.
    >
    > What is it that you want to happen when the user presses the button?
    > If there's something that needs to happen above and beyond the text
    > field's value being pushed to the model, then... just make it happen
    > in the action method (after the -commitEditing).
    >
    >
    >>
    >>
    >> One thought I had to make this relationship clear was to disable the
    >> button initially and then enable it only after the text field had
    >> been edited.  I found I could do this by using the
    >> objectDidBeginEditing: method.  By responding to that method I could
    >> use a binding that would affect the enabled state of the button.
    >>
    >> In spite of this, what I also find is it that after implementing the
    >> objectDidBeginEditing: method, the action for the button that calls
    >> commitEditing no longer works.  For some reason the control thinks
    >> that no editors need to commit any editing.  If I remove the
    >> implementation for objectDidBeginEditing: things work fine.  When I
    >> add it back in, things fail.
    >
    > Sounds like your override of -objectDidBeginEditing: is not calling
    > through to super.  You're effectively replacing the implementation
    > rather than extending it.
    >
    > That said, it sounds to me like you're approaching things in a weird way.
    >
    > Cheers,
    > Ken
    >
    >
  • On Aug 6, 2008, at 12:22 AM, Tron Thomas wrote:

    > The program is a simple game.  The player answers questions by
    > typing something in and then either pressing enter or clicking on
    > the button to commit their answer.  Although not likely it is,
    > possible the user may try to use the previous information as the
    > answer to the current question, so they might simply click the
    > button to resubmit an answer without editing anything.

    Right, and?  As far as I can see, your button's action method should
    just call -commitEditing and then proceed to do whatever it is that
    should happen when the user provides an answer.  For what it's worth,
    your text field should use the same action method.  I do _not_ think
    that the mere setting of the model value by the text field's binding
    should be taken to mean the user has finished entering their answer.
    For example, the user may simply have tabbed to another control.

    Cheers,
    Ken
  • The only the the controller's action does in response to the button
    action is call commitEditing.  I'm not sure what you mean by having the
    text field use the same action.  I don't see any action method in the
    Interface Builder connection dialog for the text field.  There is
    something called selector, I'm not sure what that is for.

    In this case, the user interface is so simple there is no need to worry
    about the user tabbing to another control.  There is no other control
    they can tab to.
    Ken Thomases wrote:
    > On Aug 6, 2008, at 12:22 AM, Tron Thomas wrote:
    >
    >> The program is a simple game.  The player answers questions by typing
    >> something in and then either pressing enter or clicking on the button
    >> to commit their answer.  Although not likely it is, possible the user
    >> may try to use the previous information as the answer to the current
    >> question, so they might simply click the button to resubmit an answer
    >> without editing anything.
    >
    > Right, and?  As far as I can see, your button's action method should
    > just call -commitEditing and then proceed to do whatever it is that
    > should happen when the user provides an answer.  For what it's worth,
    > your text field should use the same action method.  I do _not_ think
    > that the mere setting of the model value by the text field's binding
    > should be taken to mean the user has finished entering their answer.
    > For example, the user may simply have tabbed to another control.
    >
    > Cheers,
    > Ken
    >
    >
  • On Aug 6, 2008, at 9:39 PM, Tron Thomas wrote:

    > The only the the controller's action does in response to the button
    > action is call commitEditing.

    And that's the problem.  You're overloading the text field's binding
    to your model with semantics of a user action, when it's not.

    The action triggered by the button should explicitly do your game
    logic (check the user's answer, give them points, advance them to the
    next question, etc.).

    > I'm not sure what you mean by having the text field use the same
    > action.  I don't see any action method in the Interface Builder
    > connection dialog for the text field.  There is something called
    > selector, I'm not sure what that is for.

    When you hit return in a text field, it fires off an action to its
    target.  This is conceptually just like when you click a button.  In
    IB, you set up the target-action in the same way.  You control-drag
    from the text field to the target, and then select the action
    (selector) in the target.

    Note that hitting return _does_ carry the semantics of a user action,
    and so would be an appropriate occasion for your game to check the
    user's answer and give them points, or whatever.  That's why I
    recommend that the text field be set up to invoke the same action on
    the same target as the button.  Both should do the same thing.

    > In this case, the user interface is so simple there is no need to
    > worry about the user tabbing to another control.  There is no other
    > control they can tab to.

    You're wrong.  Look in System Preferences > Keyboard & Mouse >
    Keyboard Shortcuts.  There's a setting called Full Keyboard Access
    which a user can configure to allow them to tab among all controls in
    a window, not just text fields.  So, the mere existence of a button in
    the window is enough to allow the user to tab away from your text
    field.  That would update the binding but should not (in my opinion)
    count as them entering their answer.

    Cheers,
    Ken
previous month august 2008 next month
MTWTFSS
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Go to today