How to remove UIButton's image for certain states?

Joel Derfner picture Joel Derfner · Feb 5, 2013 · Viewed 9.9k times · Source

I'm working on a card game. I want the card backs to show an image and the card fronts to show the card contents. I've gotten the image to show on the back, but I can't figure out how to clear it when it's selected. (All the run-this-code-when-it's-selected code is running, so I know it's not a question of not actually changing state.) Here's my code:

-(void)updateUI {
for (UIButton *cardButton in self.cardButtons) {
    Card *card = [self.game cardAtIndex:[self.cardButtons indexOfObject:cardButton]];
    [cardButton setTitle:card.contents forState:UIControlStateSelected];
    [cardButton setTitle:card.contents forState:UIControlStateSelected | UIControlStateDisabled];
    [cardButton setImage:[UIImage imageNamed:@"cardback.png"] forState:UIControlStateNormal];

//I've tried various permutations of the following three lines, but the image never disappears.

    [cardButton setImage:nil forState:UIControlStateSelected];
    [cardButton setImage:nil forState:UIControlStateSelected | UIControlStateHighlighted];
    [cardButton setImage:nil forState:UIControlStateNormal];

    cardButton.selected = card.faceUp;
    cardButton.alpha=(card.unplayable ? 0.3:1.0);
    [self.scoreLabel setText:[NSString stringWithFormat:@"Score: %d",self.game.score]];
}
}

Any thoughts about what I'm doing wrong?

Answer

Mathew picture Mathew · Feb 5, 2013

I am guessing your problem is this (from the Apple Documentation for setImage:forState:):

In general, if a property is not specified for a state, the default is to use the UIControlStateNormal value. If the UIControlStateNormal value is not set, then the property defaults to a system value. Therefore, at a minimum, you should set the value for the normal state.

So, you cannot simply unset images for some states because the default image will get used.

I see a couple of solutions to this problem:

  1. Use a transparent image for the states you want to show through rather than nil
  2. Give your cards a UIImageView subview that you set to the card back image. If the card is face down, use setHidden: to show the subview. If the card is face up, use setHidden: to hide the subview. I would probably use the – addTarget:action:forControlEvents: method within your custom button class to register a custom method that shows or hides the card back subview.

Note that you could easily take option 2 a step further by displaying the front of the card in a subview as well and then you can easily animate a flip transition between those two subviews so that the card "flips" over when pressed.