I'm having a very specific "bug" in my iPhone application. I'm开发者_高级运维 setting two images for the highlighted and normal states of a button. It works as expected when you "press" and then "touch up" at a slow pace, but if you click/tap it quickly, there's a noticeable flicker between states. Is this a known bug or am I setting the states incorrectly?
Here's the code that creates the buttons:
UIImage *normalImage = [[UIImage imageNamed:@"btn-small.png"] stretchableImageWithLeftCapWidth:10.0f topCapHeight:0.0f];
UIImage *highlightedImage = [[UIImage imageNamed:@"btn-small-down.png"] stretchableImageWithLeftCapWidth:10.0f topCapHeight:0.0f];
[self setBackgroundColor:[UIColor clearColor]];
[self setBackgroundImage:normalImage forState:UIControlStateNormal];
[self setBackgroundImage:highlightedImage forState:UIControlStateDisabled];
[self setBackgroundImage:highlightedImage forState:UIControlStateHighlighted];
[self setAdjustsImageWhenDisabled:FALSE];
[self setAdjustsImageWhenHighlighted:FALSE];
When a button is tapped it simply disables itself and enables the other button:
- (IBAction)aboutButtonTouched:(id)sender
{
aboutButton.enabled = FALSE;
rulesButton.enabled = TRUE;
}
- (IBAction)rulesButtonTouched:(id)sender
{
rulesButton.enabled = FALSE;
aboutButton.enabled = TRUE;
}
Any thoughts on this quick-click flicker?
Ok, I resolved this. Took a bit of reverse engineering what I was trying to do, but I thought I'd post what I did in case it helps someone else.
The first thing I did was modify the aboutButtonTouched method to log the button's state property which is a bit-mask NSUInteger:
- (IBAction)aboutButtonTouched:(id)sender
{
rulesButton.enabled = TRUE;
[sender setEnabled:FALSE];
NSLog(@"%d", [sender state]);
}
At this point, the button is disabled through setEnabled, and the log reported that the state was "3". Looking at the bit-mask type for UIControlState:
enum {
UIControlStateNormal = 0, // 0
UIControlStateHighlighted = 1 << 0, // 1
UIControlStateDisabled = 1 << 1, // 2
UIControlStateSelected = 1 << 2, // 4
UIControlStateApplication = 0x00FF0000,
UIControlStateReserved = 0xFF000000
};
(Notes added since I can never remember bitwise). We can see that to get "3" (0011) we should use UIControlStateHighlighted | UIControlStateDisabled
(0001|0010 or 1|2), something which I did not have as a state in my original button definition. The key here that there's a brief time when the state is both before just being disabled ("A control enters this state when a touch enters and exits during tracking and and when there is a touch up" -- from the docs). So the final state settings for the button where it does not flicker are:
[self setBackgroundImage:normalImage forState:UIControlStateNormal];
[self setBackgroundImage:highlightedImage forState:UIControlStateDisabled];
[self setBackgroundImage:highlightedImage forState:UIControlStateHighlighted];
[self setBackgroundImage:highlightedImage forState:UIControlStateHighlighted|UIControlStateDisabled];
To be noted that, although not documented, all state combinations that contain UIControlStateHighlighted|UIControlStateDisabled
aren't valid: these are equivalent to same without UIControlStateDisabled
.
In short:
(UIControlStateHighlighted | UIControlStateDisabled) == UIControlStateHighlighted
and
(UIControlStateSelected | UIControlStateHighlighted | UIControlStateDisabled) == (UIControlStateSelected | UIControlStateHighlighted)
I found that the hard way: was setting some attributes for the Highlighted+Disabled state which was overwriting my settings for the Highlighted state. Spent half a day to track down the issue why button wasn't properly highlighting...
Maybe you should reverse the sequence of changing the buttons
- (IBAction)aboutButtonTouched:(id)sender
{
rulesButton.enabled = TRUE;
aboutButton.enabled = FALSE;
}
When you hide the one button first and then show the other, there's maybe a little gap, which creates the flicker. I think it's better to show the other button first.
精彩评论