Please note, this blog entry is from a previous course. You might want to check out the current one.
Your Set game should also report (mis)matches like Required Task #5 in the last assignment, but you’ll have to enhance this feature (to use NSAttributedString) to make it capable of working for both the Set and Playing Card games.
We could either change the way how the description string is generated, or adjust it afterwards. The first solution would mean adjusting the card-game-view controller, the second its set-card specific child class. Not wanting to break the existing playing-card code, I go for the second solution. However, it is necessary to access the flip-label property from the subclass, therefore move the property declaration from the class file to its header file:
@property (weak, nonatomic) IBOutlet UILabel *flipDescription;
After the interface has been updated by the super class, get the description string, check it holds card descriptions, and replace them by the card title:
- (void)updateUI { [super updateUI]; NSMutableAttributedString *description = [self.flipDescription.attributedText mutableCopy]; NSArray *setCards = [SetCard cardsFromText:description.string]; if (setCards) { for (SetCard *setCard in setCards) { NSRange range = [description.string rangeOfString:setCard.contents]; if (range.location != NSNotFound) { [description replaceCharactersInRange:range withAttributedString:[self titleForCard:setCard]]; } } [self.flipDescription setAttributedText:description]; } }
The mystical method checking the description text for cards is part of the card model, because the model knows best, how its description looks like and how it can be found in a text.
A valid card description contains of valid symbols, colors, shadings and number separated by colons. When such a description is found in the text create a new card and add it to the array passed as result of the method:
+ (NSArray *)cardsFromText:(NSString *)text { NSString *pattern = [NSString stringWithFormat:@"(%@):(%@):(%@):(\\d+)", [[SetCard validSymbols] componentsJoinedByString:@"|"], [[SetCard validColors] componentsJoinedByString:@"|"], [[SetCard validShadings] componentsJoinedByString:@"|"]]; NSError *error = NULL; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error]; if (error) return nil; NSArray *matches = [regex matchesInString:text options:0 range:NSMakeRange(0, )]; if (![matches count]) return nil; NSMutableArray *setCards = [[NSMutableArray alloc] init]; for (NSTextCheckingResult *match in matches) { SetCard *setCard = [[SetCard alloc] init]; setCard.symbol = [text substringWithRange:[match rangeAtIndex:1]]; setCard.color = [text substringWithRange:[match rangeAtIndex:2]]; setCard.shading = [text substringWithRange:[match rangeAtIndex:3]]; setCard.number = [[text substringWithRange:[match rangeAtIndex:4] intValue]]; [setCards addObject:setCard]; } return setCards; }
Note, that the code above only handles the flip description, not the strings provided when using the history slider … but as we will remove this slider in the following task … we don’t care at the moment …
The complete code is available on github.
Hi,
I have some questions after reading this post.
1. isn’t that flipDescription is nil now? Why can it become the parameter of cardsFromText:?
2. I do not quite understand the usage of cardsFromText: Could you explain more??
3. After you overwrote the method updateUI, where can we use the sentence like @”%@ don’t match! %d point penalty!”??
Thank you.
ad 1. flipDescription was added in assignment #2 task #5, so it should hold the description of your current/last flip.
ad 2. the “current” description text holds the information of the cards used in the current/last flip. In this task we need to make it look fence, coloring the symbols, etc … To be able to do this, we pars the description text to get the information which cards are in there to be able to change the look of the description.
ad 3. we still call the original method from the parent class: [super updateUI]
What’s this NSRegularExpression business? I guess I sort of understand making a pattern but creating an array with matchesInString:options:range: makes no sense. Also, how come we can’t pull the information from lastChosenCards? It seems a lot easier to use:
NSAttributedString *aString = [self titleForCard:[self.game.lastChosenCards objectAtIndex:0]];
self.flipDescription.attributedText = aString;
I’m having trouble figuring out how to get flipDescription to show a line such as “(some card) and (some card) do not match! -4 points!”… Am I way off track here?
Please note, this is a solution, it’s not the solution. Of course you can handle it using other methods. You could even hard code the symbols, etc.
Thanks for your great explanations! It really helps to get through this tough tutorial… Your reset game button solution was very simple and elegant.
However I found this solution to be maybe your most convoluted so far. I totally did not understand your logic.
My question is, when I simply copy and paste your code into the project, I get an error on lines 19-21 of cardsFromText: saying:
setCard.symbol = 1];
setCard.color = 1];
setCard.shading = 1];
“implicit conversion of ‘int’ to ‘NSString *’ is disallowed with ARC”. I tried removing the brackets of course, still errors. What am I missing?
Thanks for pointing this out, it seems the WordPress thought part of the Objective-C code to be instructions to WordPress and processed them … you should be able to see the correct version now!
… you can find the correct code in the github repository.
i do this task by overriding updateTouchCardButtonDescriptionLabel -flipDescription- method and by make use of cardButtonTitle -titleForCard-
– (void)updateTouchCardButtonDescriptionLabel
{
NSMutableAttributedString * joinedString=[[NSMutableAttributedString alloc]init];
NSAttributedString* result;
for (id card in [self.game cardsTryMatching]) {
NSAttributedString * cardDrawing=[[NSAttributedString alloc]initWithAttributedString:[self cardButtonTitle:card]];
[joinedString appendAttributedString:cardDrawing];
}
if (self.game.matchOtherCardsScore > 0) {
result=[[NSAttributedString alloc]initWithString:[NSString stringWithFormat:@” Matched for %ld points!”,(long)self.game.matchOtherCardsScore]];
[joinedString appendAttributedString:result];
self.lastConsiderationLabel.attributedText=joinedString;
}
else if (self.game.matchOtherCardsScore 0) {
self.lastConsiderationLabel.text = [NSString stringWithFormat:@”Matched %@ for %ld points!”,[[self.game cardsTryMatching] componentsJoinedByString:@”,”],
(long)self.game.matchOtherCardsScore];
}
else if (self.game.matchOtherCardsScore < 0) {
self.lastConsiderationLabel.text = [NSString stringWithFormat:@"%@ don't match! %ld points penalty!",[[self.game cardsTryMatching] componentsJoinedByString:@","],
(long)self.game.matchOtherCardsScore];
}
else {
self.lastConsiderationLabel.text = [[self.game cardsTryMatching] componentsJoinedByString:@","];
}
}
i hope it's useful 🙂