nupac
nupac

Reputation: 2489

Changing UIButton(in this case a playing card)'s title randomly

New to ios programming so for practice I am trying to make a simple app with a playing card with it's back on the view which you can tap to reveal the front of the card. The front of the card is different every time you tap it. What I am trying to achieve is that when i tap the card:

  1. it flips and displays a random card,
  2. have a label that increments by 1 every time the card is flipped

I am done with the second requirement but I can't seem to get the first one right. I have 3 models(Card, Deck, PlayingCard) My Deck model is responsible for randomising the cards and here are the codes.

Deck.h

#import <Foundation/Foundation.h>
#import "Card.h"

@interface Deck : NSObject
- (void)addCard:(Card *)card atTop:(BOOL)atTop;

- (Card *)drawRandomCard;

@end

Deck.m

#import "Deck.h"
@interface Deck()

@property (strong, nonatomic) NSMutableArray *cards;

@end

@implementation Deck
-(NSMutableArray *)cards
{
    if(!_cards) _cards =[[NSMutableArray alloc] init];
    return _cards;
} 

- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
    if(card){
        if(atTop){
            [self.cards insertObject:card atIndex:0];
        }
        else
        {
            [self.cards addObject:card];
        }
    }
} 

- (Card *)drawRandomCard
{
    Card *randomCard = nil;  

    if(self.cards.count){
        unsigned index = arc4random() % self.cards.count;
        randomCard = self.cards[index];
        [self.cards removeObjectAtIndex:index];
    }

    return randomCard;
}
@end

My PlayingCard basically describes the contents of a Card. for eg. Jack of Hearts, 2 of Diamonds where Jack would be the "rank" of the card and "hearts" would be the suit of the card which together form the contents of the card.

PlayingCard.h

#import <Foundation/Foundation.h>
#import "Card.h"
@interface PlayingCard : NSObject

@property (strong, nonatomic) NSString *suit;
@property (nonatomic) NSUInteger   rank;

+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;

- (NSString *)contents;
@end

PlayingCard.m

#import "PlayingCard.h"

@implementation PlayingCard

- (NSString *)contents
{
    NSArray *rankStrings = [PlayingCard rankStrings];
    return [rankStrings[self.rank] stringByAppendingString:self.suit];
}

@synthesize suit = _suit;

+(NSArray *)validSuits{
    return @[@"♥", @"♦", @"♠", @"♣"] ;
}

+(NSArray *)rankStrings{
    return  @[@ "?", @"A", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9",@"10", @"J", @"Q", @"K"];

}
-(void)setSuit:(NSString *)suit
{
    if([[PlayingCard validSuits] containsObject:suit]){
        _suit = suit;
    }
}

-(NSString *)suit{
    return _suit ? _suit: @"?";
}

+ (NSUInteger)maxRank {
    return [self rankStrings].count-1;
}

-(void)setRank:(NSUInteger)rank{
    if(rank <= [PlayingCard maxRank]) {
        _rank = rank;
    }
}
@end

Finally my ViewController.m

#import "CardGameViewController.h"
#import "Card.h"
#import "Deck.h"
#import "PlayingCard.h"

@interface CardGameViewController ()
@property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
@property (nonatomic) int flipCount;
@property (nonatomic) NSString *title;
@property (weak, nonatomic) IBOutlet UIButton *cardRandom;
@end

@implementation CardGameViewController

- (void)setFlipCount:(int)flipCount
{
    _flipCount = flipCount;
    self.flipsLabel.text = [NSString stringWithFormat:@" Flips:%d", self.flipCount ];
}

- (IBAction)flipCard:(UIButton *)sender
{

    if(sender.isSelected) {
        sender.selected = NO;
        self.flipCount ++;
    }
    else{
        sender.selected = YES;
        self.flipCount ++;         
    }
}

@end

I researched about this and i found that I should probably use

- (void)setTitle:(NSString *)title forState:(UIControlState)state

where the state will be UIControlStateSelected because the front of the card containing the contents is the selected content of the button but I dont know how to set the title according to the random card content developed by my model.

Upvotes: 0

Views: 867

Answers (2)

KacireeSoftware
KacireeSoftware

Reputation: 808

You aren't initializing your playing card deck. Your ViewController.m file should look like this (I also reset the deck once all cards have been shown in the flipCard method):

    #import "CardViewViewController.h"
    #import "PlayingCardDeck.h"

    @interface CardViewViewController ()
    @property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
    @property (nonatomic) int flipCount;
    @property (nonatomic) PlayingCardDeck *myDeck;
    @end
    @implementation CardViewViewController
    -(PlayingCardDeck *)myDeck
    {
        if(!_myDeck) _myDeck=[[PlayingCardDeck alloc] init];
        return _myDeck;
    }

    -(void)setFlipCount:(int)flipCount
    {
        _flipCount= (int) flipCount;
        self.flipsLabel.text=[NSString stringWithFormat:@"Flips: %d",self.flipCount];
    }
    - (IBAction)flipCard:(UIButton *)sender
    {
        if (!sender.isSelected)
        {
            if (self.flipCount >= 52)
            {
                self.flipCount = 0;
                self.myDeck = nil;
            }
            Card *cCard = [self.myDeck drawRandomCard];
            [sender setTitle: [cCard contents] forState:UIControlStateSelected];
            self.flipCount++;
        }
        sender.selected = !sender.isSelected;
    }

@end

Upvotes: 0

Dhruv Goel
Dhruv Goel

Reputation: 2775

Lets assume you have a property of the class Deck (called cardDeck) in your CardGameViewController, and you have already populated that property with cards using addCard method. Now when the user taps flip button, you should do something like this ::

- (IBAction)flipCard:(UIButton *)sender
{

  //draw a random card
  Card *randomCard = [self.cardDeck drawRandomCard];

  //set the button title
  [sender setTitle:[randomCard contents] forState:UIControlStateSelected];

  if(sender.isSelected) {
    sender.selected = NO;
    self.flipCount ++;
  }
  else{
    sender.selected = YES;
    self.flipCount ++;         
  }
}

Hope this helps

PS :: drawRandomCard method has a return type "Card", it seems it should be "PlayingCard", or the PlayingCard class should be named Card.

Upvotes: 1

Related Questions