views:

263

answers:

1

I have working snippet along the lines of:

for (UIButton *b in buttonMapping) {
 [b setTitle:@"yo!" forState:UIControlStateNormal];
 [NSThread sleepForTimeInterval:1.0];
}

There's four buttons, all four buttons update. However, instead of updating one per second, four seconds go by & they all update.

How can I force UIButton to update? Or is this not the recommend method for sleeping?

+3  A: 

[b setNeedsDisplay];

I'd also not recommend sleeping the main thread (like you're doing here), since that disables all user interaction.

There are a couple of alternatives. One might be to use an NSTimer to execute a particular method once a second. However, the easier method would be to do something like:

for (NSUInteger idx = 0; idx < [buttonMapping count]; idx++) {
  UIButton * b = [buttonMapping objectAtIndex:idx];
  [b performSelector:@selector(setNormalStateTitle:) withObject:@"yo!" afterDelay:(idx*60)];
}

Then add a method to UIButton (ie, a category) called setNormalStateTitle: that simply executes the setTitle:forControlState: method. With this approach, you won't need the setNeedsDisplay method at all.

Dave DeLong
It doesn't look like UIButton responds to setNeedsDisplay.
Bill
apologies, it should just be `setNeedsDisplay`, not `setNeedsDisplay:YES`. Edited answer.
Dave DeLong
Thanks. Changing to setNeedsDisplay compiles, however the behavior is the same. Four seconds pass before any button is visibly changed.
Bill
@Bill then I'd go with the `performSelector:withObject:afterDelay:` approach. Because it really isn't a good idea to sleep your main thread.
Dave DeLong
That would work for this example, but really what I'm trying to do is show the user a sequence of buttons to press (a la the simple simon game of old). So instead of setting title, what I really want to do is highlight the button for a few hundred milliseconds, de-highlight it, and then move on to the next button. It's actually to cool to learn the performSelector method, but would make things a lot more complicated here.
Bill
@Bill that's still possible using the `setHighlighted:` selector. Here's another option to consider: split off a second thread that has your loop and your `sleep()` calls, but instead of modifying the object directly, have it update the GUI on the main thread via `performSelectorOnMainThread:withObject:waitUntilDone:`
Dave DeLong
@Dave How do I go from calling the class method (for the new thread) back to the instance methods (for updating the UI)?
Bill
Bill: Make the thread method an instance method. There are not many reasons to make a class method in Objective-C—the only solid one I can think of is the mixed-names-and-values syntax, and if you're doing a thread perform, you don't have the problem that that solves.
Peter Hosey