Detecting Text Field Focus Changes In Mac OS X (NSTextField or NSTextView)
©2006 Darel Rex Finley. This complete article, unmodified, may be freely distributed for educational purposes.



Although Mac OS X provides developers with a way to invoke execution of their own functions on certain kinds of events, a change in text field focus does not seem to be included as one of those events. Nor does there seem to be a straightforward way of checking to see whether a particular text field has focus right now.

Here’s an Objective-C way to do it that appears to work, and is compatible with both single-line NSTextField objects and multi-line NSTextView objects. (Some of this code was copied from another site or manual — I’ve forgotten where — so I don’t claim to be its sole author. To the extent that this code is mine, I donate it to the public domain.)

bool hasFocus(id theField) {
  return   [[[theField window] firstResponder] isKindOfClass:[NSTextView class]]
  &&        [[theField window] fieldEditor:NO forObject:nil]!=nil
  && ( (id) [[theField window] firstResponder]          ==theField
  ||  [(id) [[theField window] firstResponder] delegate]==theField); }

To see whether an NSTextField or NSTextView has focus, simply pass the field’s id to hasFocus, and it will return YES or NO.

How do you make your app react to a change in focus among text fields? The only way I have found to do this is to call hasFocus over and over to see if anything has changed. If that is implemented badly, it will make your app sluggish and unresponsive, and needlessly hog the processor from other apps. To avoid that mistake, you should set up an NSTimer object that fires, say, ten times per second, and invokes a function that uses hasFocus to check for changes. That might look something like this:

//  (Globals.)
int       fieldActive=NONE ;
NSTimer  *theTimer ;



//  Start the repeating timer.  (This code should be executed only
//  once, when the app first starts up.)

theTimer=[NSTimer scheduledTimerWithTimeInterval:.1 target:self
selector:@selector(handleTimerPulse:) userInfo:nil repeats:YES];



- (void) handleTimerPulse:(id) timer {

  //  Handle textfield focus changes.
  if (fieldActive!=FIELD_A && hasFocus(fieldA)) {
    fieldActive   =FIELD_A;
    do something in reaction to field A getting focus; }
  if (fieldActive!=FIELD_B && hasFocus(fieldB)) {
    fieldActive   =FIELD_B;
    do something in reaction to field B getting focus; }}

(The above example assumes you care only when a field gets focus, not when it loses focus, but with a few more lines of code it can handle that too.)

But wait a minute! Isn’t that polling, and isn’t polling a bad, bad thing that you’re never supposed to ever do? Well, not exactly. Polling got a bad rap in the early days of computers due to some programmers writing code like this:

WHILE app is still running
    IF there is user activity THEN
        handle the user activity
    END IF
END WHILE

That code is indeed really bad, because it causes your app to use as much CPU time as the OS will allow, even when the user isn’t doing anything! But that’s nothing like the NSTimer-based technique we’re using here, which uses almost zero CPU even on the oldest OS X-capable computers, and will only use less and less as processors get faster (and do so in only one CPU core). Some people just don’t like the idea of polling, and I’d certainly be with them if the notification system would tell you when an NSTextField or NSTextView got input focus. The closest thing to it is controlTextDidBeginEditing, but alas — that notification goes out only when the user changes the text.

(Update: I’ve been told that you can get around this notification-system oversight by subclassing NSTextField and NSTextView, and putting your own code into the becomeFirstResponder method — in effect creating your own notification for this event. Some developers might prefer that to the NSTimer technique.)

Background processing

You can also put other code in your timer-handling function, if you want your app to do anything in the background, such as incrementally updating an animation or a heavy number-crunching operation. This is one way to keep your fields, buttons, windows, and menu bar fully responsive even while your app is doing something processor-intensive. Just be sure to quit whatever you’re doing before the next firing of the timer, which can be accomplished like this:

- (void) handleTimerPulse:(id) timer {

  NSDate  *startTime=[NSDate date] ;

  //  Handle textfield focus changes.
  if (fieldActive!=FIELD_A && hasFocus(fieldA)) {
    fieldActive   =FIELD_A;
    do something in reaction to field A getting focus; }
  if (fieldActive!=FIELD_B && hasFocus(fieldB)) {
    fieldActive   =FIELD_B;
    do something in reaction to field B getting focus; }

  //  Perform background processing work.
  while (-[startTime timeIntervalSinceNow]<.095 && work is needed) {
    do an incremental bit of work that takes
    substantially less than .005 seconds
; }}

Another — probably better — way to perform background processing without hurting your app’s responsiveness to user actions is to spawn a separate thread of execution in which the background processing can take place. Visit this page for information on how to do that, and also read about processor count and NSLock.

Bear in mind that, as of this writing, I’m pretty new to OS X development. So if you see anything obviously wrong (or seriously improvable) in this page, please do let me know! Just send me an e-mail.

Does the brace style in the above code samples freak you out? Click here to see it explained in a new window.

Quicksort  |  Point in polygon  |  Mouseover menus  |  Gyroscope  |  Osmosis  |  Polarizer experiment  |  Gravity table equilibrium  |  Calculus without calculus  | Overlapping maze