Содержание
Текст Домашнего задания на английском языке доступен на iTunes в пункте “Developing iOS 7 app:Assignment 1″. На русском языке
Пункт 1
Следуйте пошаговой инструкции на слайдах лекции (отдельный документ) для построения и запуска Matchismo на симуляторе iPhone (iPhone Simulator) в Xcode 5. Не переходите к следующим шагам до тех пор, пока ваша карта не будет переворачиваться “лицевой” и обратной стороной без ошибок и предупреждений
Вторая часть слайдов Лекции 2 содержит пошаговую инструкцию для выполнения демонстрационного проекта, в котором создаются классы для Домашнего задания. Следуя этой пошаговой инструкции, вы полностью выполните пункт 1 первого домашнего задания. Рекомендуется не пользоваться copy/paste, а набирать весь код вручную для лучшего освоения Xcode.
Если у вас все-таки возникнут проблемы с кодом, вы можете найти его на Github.
Пункт 2
Убедитесь, что вы включили в приложение весь приведенный на первых двух лекциях код всех четырех классов: Card, Deck, PlayingCard и PlayingCardDeck. Вы должны напечатать этот код (а не копировать и вставлять его откуда-то еще). Цель этого обязательного задания состоит в получении опыта редактирования кода в Xcode и освоении синтаксиса Objective-C.
Начните вводить в ваш проект классы, представленные в пошаговой инструкции воспроизведения демонстрационного проекта. Основная структура класса Card уже добавлена в течение Лекции №2. Сформируйте public interface этого класса и метод match:.
[objc highlight=»1,2,6,15,16,18,20,23,25″]
// Card.h
// Matchismo
#import <Foundation/Foundation.h>
@interface Card : NSObject
@property (strong,nonatomic) NSString *contents;
@property (nonatomic,getter = isChosen) BOOL chosen;
@property (nonatomic,getter = isMatched) BOOL matched;
-(int)match:(NSArray *)otherCards;
@end
// Card.m
// Matchismo
#import "Card.h"
@interface Card ()
@end
@implementation Card
-(int)match:(NSArray *)otherCards
{
int score =0;
for (Card *card in otherCards) {
if ([card.contents isEqualToString:self.contents]) {
score = 1;
}
}
return score;
}
@end
[/objc]
Добавьте новый класс Deck для колоды карт. Сформируйте его public interface (не забудьте включить класс Card для доступа к нему внутри нового класса) и реализуйте его методы, а также getter для lazy instantiation (отложенного получения экземпляра класса).
[objc highlight=»1,2,7,15,16,20,21,24,26,34,43,48″]
// Deck.h
// Matchismo
#import <Foundation/Foundation.h>
#import "Card.h"
@interface Deck : NSObject
— (void)addCard:(Card *)card atTop:(BOOL)atTop;
— (void)addCard:(Card *)card;
— (Card *)drawRandomCard;
@end
// Deck.m
// Matchismo
#import "Deck.h"
@interface Deck ()
@property (strong,nonatomic) NSMutableArray *cards; // of Card
@end
@implementation Deck
— (NSMutableArray *)cards
{
if (!_cards) {
_cards = [[NSMutableArray alloc] init];
}
return _cards;
}
— (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
— (void)addCard:(Card *)card
{
[self addCard:card atTop:NO];
}
-(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
[/objc]
Добавьте еще один новый класс PlayingCard как сабкласс (subclass) класса Card. Сформируйте его public interface и реализуйте его методы. Не забудьте сделать synthesize для suit свойства, так как используются оба его setter & getter.
[objc highlight=»1,2,6,15,16,20,23,29,31,36,43,48,53,58,65″]
// PlayingCard.h
// Matchismo
#import "Card.h"
@interface PlayingCard : Card
@property (strong, nonatomic) NSString *suit;
@property (nonatomic) NSUInteger rank;
+ (NSArray *)validSuits;
+ (NSUInteger)maxRank;
@end
// PlayingCard.m
// Matchismo
#import "PlayingCard.h"
@implementation PlayingCard
— (NSString *)contents
{
NSArray *rankStrings = [PlayingCard rankStrings];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
@synthesize suit =_suit; // потому что мы реализовали setter И getter
+ (NSArray *)validSuits
{
return @[@"♥️", @"♦️", @"♠️", @"♣️"];
}
-(void)setSuit:(NSString *)suit
{
if ([[PlayingCard validSuits] containsObject:suit]) {
_suit =suit;
}
}
— (NSString *)suit
{
return _suit ? _suit : @"?";
}
+ (NSArray *)rankStrings
{
return @[@"?", @"A", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10", @"J", @"Q", @"K"];
}
+ (NSUInteger)maxRank
{
return [[self rankStrings] count]-1;
}
-(void)setRank:(NSUInteger)rank
{
if (rank<= [PlayingCard maxRank]) {
_rank = rank;
}
}
-(int)match:(NSArray *)otherCards
{
int score =0;
int numMatches = 0;
if ([otherCards count] > 0)
{
for (int i = 0; i < [otherCards count]; i++) {
id otherCard1 = otherCards[i];
if ([otherCard1 isKindOfClass:[PlayingCard class]])
{
PlayingCard *card1 = (PlayingCard *)otherCard1;
for (int j = i+1; j < [otherCards count]; j++) {
id otherCard2 =otherCards[j];
if ([otherCard2 isKindOfClass:[PlayingCard class]])
{
PlayingCard *card2 =(PlayingCard *)otherCard2;
// check for the same suit
if ([card1.suit isEqualToString:card2.suit]) {
score += 1;
numMatches++;
}
// check for the same rank
if (card1.rank == card2.rank) {
score += 4;
numMatches++;
}
}
}
}
}
if (numMatches < [otherCards count] -1) score = 0;
}
return score;
}
@end
[/objc]
Пункт 3
Добавьте private свойство типа Deck * к CardGameViewController.
Импортируйте класс Deck и добавьте свойство Deck.
[objc highlight=»5,11″]
// CardGameViewController.m
// Matchismo
#import "CardGameViewController.h"
#import "Deck.h"
@interface CardGameViewController ()
@property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
@property (nonatomic) int flipCount;
@property (nonatomic, strong) Deck *deck;
@end
[/objc]
Код доступен на Github.
Пункт 4
Используйте паттерн “отложенное получение экземпляра класса” (lazy instantiation) для размещения (allocate) и инициализации (initialize) этого свойства ( в getter этого свойства) таким образом, чтобы оно стартовало с полной колодой игральных карт типа PlayingCardDeck.
Используем getter для проверки того, инициализировано ли свойство deck. Если нет, то выполним размещение в «куче» и инициализацию …
[objc highlight=»4,16″]
// CardGameViewController.m
#import "CardGameViewController.h"
#import "PlayingCardDeck.h"
@interface CardGameViewController ()
@property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
@property (nonatomic) int flipCount;
@property (nonatomic, strong) Deck *deck;
@end
@implementation CardGameViewController
— (Deck *)deck
{
if (!_deck) {
_deck = [[PlayingCardDeck alloc] init];
}
return _deck;
}
…..
[/objc]
Код доступен на Github.
Пункт 5
До сих пор Matchismo показывало A♣ снова и снова. Усовершенствуйте Matchismo таким образом, чтобы при повороте “лицевой” стороной оно показывало другую случайную карту, вынимаемую из колоды, описанной выше свойством Deck *. Другими словами, Matchismo должна перевернуть всю колоду игральных карт случайным образом, показывая в каждый момент времени только одну карту.
Так как мы «сдаем» случайные карты, мы не должны стартовать с A♣ (Туза пик), а должны стартовать с карты, перевернутой обратной стороной. Мы могли бы сделать это различными способами, но поскольку это всего лишь первое домашнее задание, то мы выберем самый простой: на storyboard уберем текст с кнопки и установим background image в «cardback«.
Когда карта переворачивается, то в зависимости от того, какую сторону карты («лицевую» или обратную) мы кликали будут производиться разные действия: если это была «лицевая» сторона, то мы просто должны показать теперь обратную сторону, ну, а если это была обратная сторона, то мы должны показать новую карту из колоды. Сторону карты мы будем определять по длине текста заголовка кнопки [sender.currentTitle length]
, а новую карту из колоды будем получать с помощью метода drawRandomCard
:
[objc highlight=»4,9″]
. . . . . . . .
— (IBAction)touchCardButton:(UIButton *)sender
{
if ([sender.currentTitle length]) {
[sender setBackgroundImage:[UIImage imageNamed:@"cardback"]
forState:UIControlStateNormal];
[sender setTitle:@"" forState:UIControlStateNormal];
} else {
Card *card = [self.deck drawRandomCard];
if (card){
[sender setBackgroundImage:[UIImage imageNamed:@"cardfront"]
forState:UIControlStateNormal];
[sender setTitle:card.contents forState:UIControlStateNormal];
}
}
self.flipCount++;
}
. . . . . . . .
[/objc]
Мы подсчитываем перевороты карты в обе стороны: и с обратной на «лицевую», и с «лицевой» на обратную, так что для колоды в 52 карты мы получим 104 переворота, а потом карта не будет переворачиваться на «лицевую» сторону.
Код можно получить на Github
Пункт 6
Не разрушайте существующей функциональности Matchismo (например, метка Flips для расчета переворотов должна продолжать их считать).
… выполнено.
При card.contents бывает проскакивают одни масти (без чисел).
Понял в чем трабл, извиняюсь
А в чем? Намекните.
В этом тексте не приведена реализация PlayingCardDeck. На GitHub она есть.
У меня там вроде многоточие стоит и подразумевается, что в посте представлен код неполностью. Вы думаете надо добавить реализацию PlayingCardDeck в текст поста?
Ну я думаю, стоит, по крайней мере, в пункте 2 в конце хотя бы упомянуть. А то я дошла до пункта 4, стала оттуда код переписывать — и смотрю, класса-то нет. Случился небольшой ступор.
кстати, зачем тут приведена
-(int)match:(NSArray *)otherCards для класса PlayingCard? Это профессор забежал вперед ?
Спасибо за замечание. Постараюсь исправить в ближайшее время.
По поводу дополнительного кода: вообще-то Домашнее Задание выполняет студент, то есть я, а профессор читает Лекции и код, по-моему, лучше брать сначала с Лекций, а потом самому уже выполнять те пункты, которых нет в Лекции.
Так что эта строка могла появиться и у меня на перепутье от Задания 1 к Заданию 2, например.