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?
In case you stink at Objective C like me. This is Sharon and MultiColourPixel's solution implemented in Swift.
func removeImageIfSelected(myButton: UIButton) {
if myButton.isSelected {
//set nil for .normal so there isn't a default value for Swift to use
self.setImage(nil, for: .normal)
} else {
self.setImage(UIImage(systemName: "checkmark.circle.fill"), for: .normal)
}
}
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 theUIControlStateNormal
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:
nil
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.
A quick way to create a transparent image for the other states, since nil will default to the value of UIControlStateNormal, is to allocate and assign an empty image:
UIImage *cardBackImage = [UIImage imageNamed:@"cardback.png"];
UIImage *cardFrontImage = [[UIImage alloc] init];
[cardButton setImage:cardBackImage forState:UIControlStateNormal];
[cardButton setImage:cardFrontImage forState:UIControlStateSelected];
[cardButton setImage:cardFrontImage forState:UIControlStateSelected|UIControlStateDisabled];
If you use setBackgroundImage, that will be fine. here is my code.
- (void)updateUI
{
for (int i=0; i < self.cardButtons.count; i++) {
Card *card = [self.game cardAtIndex:i];
UIButton *cardButton = self.cardButtons[i];
[cardButton setTitle:card.contents forState:UIControlStateSelected];
[cardButton setTitle:card.contents forState:UIControlStateSelected|UIControlStateDisabled];
cardButton.selected = card.isFaceup;
cardButton.enabled = !card.isUnplayable;
cardButton.alpha = card.isUnplayable ? 0.3 : 1.0;
//handle flipcard backgroundimage changes;
UIImage *backgroundImage = (!cardButton.selected) ? [CardGameViewController cardBackgroundImage] : nil;
[cardButton setBackgroundImage:backgroundImage forState:UIControlStateNormal];
[self updateScoreLabel:self.game.score];
NSArray *matchCards = [self.game.matchCards lastObject];
if (matchCards) {
UIColor *color = [self getOneMatchColor];
for (NSNumber *matchCardIndex in matchCards) {
NSUInteger index = [matchCardIndex integerValue];
UIButton *matchCardButton = self.cardButtons[index];
if (color) matchCardButton.backgroundColor = color;
}
[self.game.matchCards removeLastObject];
}
}
}
The simplest solution I found to this was to check if the button was flagged up as selected. If not, apply a background image, else apply a null UIImage.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With