Please note, this blog entry is from a previous course. You might want to check out the current one.
As in a real game of Set, the user should start with 12 cards and then have the option of requesting 3 more cards to be dealt at any time if he or she is unable to locate a Set. Do something sensible when there are no more cards in the deck.
Add a new button in storyboard and set its tag to 3 (the number of cards that should be added):
Create an outlet and an action for the new button. When the button is touched add the number of new cards to the game indicated by the tag of the button. If the deck is empty afterwards, disable the button.
@property (weak, nonatomic) IBOutlet UIButton *addCardsButton; ... - (IBAction)touchAddCardsButton:(UIButton *)sender { for (int i = 0; i < sender.tag; i++) { [self.game drawNewCard]; } if (self.game.deckIsEmpty) { sender.enabled = NO; sender.alpha = 0.5; } [self updateUI]; }
When dealing a new deck, make sure there is no old card view on screen, reset the card array, the grid and the add-cards button:
- (IBAction)touchDealButton:(UIButton *)sender { ... for (UIView *subView in self.cardViews) { [subView removeFromSuperview]; } self.cardViews = nil; self.grid = nil; self.addCardsButton.enabled = YES; self.addCardsButton.alpha = 1.0; ... }
When updating the user interface move the grid calculations to a loop of its own after you reset the grid to a possible new card count:
- (void)updateUI { ... self.grid.minimumNumberOfCells = [self.cardViews count]; for (NSUInteger viewIndex = 0; viewIndex < [self.cardViews count]; viewIndex++) { CGRect frame = [self.grid frameOfCellAtRow:viewIndex / self.grid.columnCount inColumn:viewIndex % self.grid.columnCount]; frame = CGRectInset(frame, frame.size.width * CARDSPACINGINPERCENT, frame.size.height * CARDSPACINGINPERCENT); ((UIView *)self.cardViews[viewIndex]).frame = frame; } ... }
The code above introduced two new public methods for the card-matching-game model:
- (void)drawNewCard; @property (nonatomic, readonly) BOOL deckIsEmpty;
… and it also needs to store the deck (which was discarded previously after the initialization):
@property (strong, nonatomic) Deck *deck; ... - (instancetype)initWithCardCount:(NSUInteger)count usingDeck:(Deck *)deck { ... _deck = deck; ... }
The two new methods just add a new card from the deck to the game, and check if the deck is empty by trying to draw another card:
- (void)drawNewCard { Card *card = [self.deck drawRandomCard]; if (card) { [self.cards addObject:card]; } } - (BOOL)deckIsEmpty { Card *card = [self.deck drawRandomCard]; if (card) { [self.deck addCard:card]; return NO; } return YES; }
When adding lots of new cards, the cards get smaller and smaller which uncovered a problem with a fixed border radios of the set cards, which can easily be adjusted to be relative to the actual card size:
#define CORNER_RADIUS 0.1 - (void)drawRect:(CGRect)rect { UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.bounds.size.width * CORNER_RADIUS]; ... }
The complete code is available on github.
I wonder : why must the updateUI be changed? What was wrong with
CGRect frame = [self.grid frameOfCellAtRow:viewIndex / self.grid.columnCount
inColumn:viewIndex % self.grid.columnCount];
frame = CGRectInset(frame, frame.size.width * CARDSPACINGINPERCENT, frame.size.height * CARDSPACINGINPERCENT);
cardView.frame = frame;
Because upon calling the updateUI the code iterates through all the card indices and consequently the frames of the views are being changed. Tx for your answer once more.
… you really ask tough questions 😉 which show that I could have been more detailed with my explanations …
Up to now (before the assignment #4 task #4) we only removed cards, and had just more empty space on screen. Now we add new cards! If there is not enough empty space for those new cards, they would be placed outside the screen and not accessible. Thus I added after adding and removing the cards an additional loop to resize the grid for the current count of cards …
How about replace self.grid.minimumNumberOfCells = [self.cardViews count];
by
self.grid.minimumNumberOfCells = self.game.numberOfDealtGame;
then move this to the first line of updateUI.
We can afterwards remove the additional loop.
When you remove a card from the view (because they matched), the number of cards in the view changes, the number of dealt cards does not.
Yes, you are right!
When I add the cards to Set game, it is going to draw them out of the frame. Does anyone has the same problem?