1

My MacOS Cocoa application displays a window of static text, meaning it should not be changed by the user, should not be first responder, etcetera. The only thing that happens to the text is that each word of it changes color (from "idleColor" to "highlightColor", and then back again) at a specific point in time. It is similar to a Karaoke display - individual words change color, and then change back, under program control, based on a list of timed events.

All of this works beautifully under MacOS 10.7 and 10.8. BUT, under 10.9, the text color does NOT change UNLESS I click in the window and continually move the cursor around, so I am manually highlighting (and un-highlighting) some of the text, continuously. If I do this, the regular words behave as intended. Essentially, it feels like the OS is refusing to update the window under program control, unless I am forcing it to update by manually performing something that requires the UI to respond.

The code that performs the color changes is as follows:

if (sEvent.attribute == HIGHLIGHT_ON) {
    [sTextView setTextColor:highlightColor range: currentRange];
    textIsLitUp = YES;
    }
else {
    [sTextView setTextColor:idleColor range: currentRange];
    textIsLitUp = NO;
    }
[sTextView setNeedsDisplay:YES];

(sTextView is a subclass of NSTextView.)

Now, if I comment out that last line, then I get the same, incorrect behavior under 10.7 and 10.8. In other words, under 10.9, the setNeedsDisplay method is not working, or not working the same way.

Does anyone have any ideas about working around this, or have any other light to shed on the problem? Or am I doing something terribly wrong? It is CRITICAL to the application that the changes to the textColor happen without latency!

EDITING MY QUESTION - to answer it:

Found the answer elsewhere here! I needed to call setNeedsDisplay on the main thread - it was in a secondary thread. The weird thing is that it always seemed to work fine under 10.7 and 10.8. It only broke under 10.9. So I just changed this:

[myTextField setNeedsDisplay:YES];

To this:

dispatch_async(dispatch_get_main_queue(), ^{[myTextField setNeedsDisplay:YES];});

…and it seem to have worked. Hope this helps someone else…

user1912017
  • 7
  • 1
  • 2
  • Have you tried setting needs display on the superview of sTextView? – Paige DePol Jan 28 '14 at 03:35
  • Found the answer here: http://stackoverflow.com/questions/13916331/setneedsdisplay-not-working?rq=1 I needed to call setNeedsDisplay on the main thread - it was in a secondary thread. The weird thing is that it always seemed to work fine under 10.7 and 10.8. It only broke under 10.9. – user1912017 Jan 28 '14 at 03:47
  • Ah yes, that would do it. You didn't mention you were threading or I would of suggested that as well. Glad you found the solution! :) – Paige DePol Jan 28 '14 at 03:55
  • Not as glad as I am! :-) Thanks! – user1912017 Jan 28 '14 at 03:57
  • You may want to answer your question with an actual answer instead of edit, which you can then accept. That way the question will become marked as answered, which can help people see there is a solution. – Paige DePol Jan 28 '14 at 05:51
  • Yeah, I tried that, but SO makes us noobs wait (or some thing like that) before answering our own question, and it SUGGESTS that I edit the original instead. SO has totally save my butt several times, but it's got its own not-exactly-intuitive etiquette. (Not complaining - just trying not to get yelled at!) Thanks to all who participate here! – user1912017 Jan 28 '14 at 16:33

1 Answers1

2

You don’t want to do any of the changing of AppKit objects in non-main threads—it’ll work sometimes, maybe even often, but then every once in a while it’ll crash, and you’ll wonder why. So:

[sTextView setTextColor:idleColor range: currentRange];

needs to be on the main thread, too.

Wil Shipley
  • 9,343
  • 35
  • 59
  • Wow, thanks! I HAVE seen crashes, always deep in some glyph-rendering routine. They're infrequent, but they happen more if I "push" the program hard by making it run faster, with many "events" per second. I need a separate thread to run a tight loop that manages the list of events, so that my UI stays responsive on the main thread. These methods called on the NSTextView are happening a lot! So this begs the question: Might I see any performance hit by "sending" those method calls from the main thread, instead of the second thread that figures out when to send them? Either way, I'll try it. – user1912017 Jan 28 '14 at 16:51