Stanford CS 193P iOS 7 — Домашнее задание 2

Screen Shot 2014-07-13 at 3.34.28 PMСодержание

Текст Домашнего задания на английском языке доступен на  iTunes в пункте  “Developing iOS 7 app:Assignment 2″На русском языке 

Задание 2 Matchismo 2 fall 2013.pdf

Пункт 1

Следуйте детальной инструкции на слайдах лекции (отдельный документ) для воссоздания последней версии Matchismo, которую мы построили на лекции (то есть вариант с множеством карт) и запустите его на симуляторе iPhone (iPhone Simulator) в Xcode 5. Не переходите к следующим шагам до тех пор, пока ваша карточная игра “на совпадение” не будет функционировать без ошибок и предупреждений.

 

Слайды  Лекции 3 содержат подробную пошаговую инструкцию для выполнения Домашнего задания 1 и для всех шагов, описанных выше. Следуя этой инструкции (если вы уже не сделали это в течение лекции), вы полностью выполните пункт 2 Домашней работы 2.

Полный код для этого пункта доступен на Github.

Пункт 2

Добавьте кнопку UIButton для “пересдачи” карт (то есть для начала новой игры). Она должна переустановливать счет (метку score или что-то еще в UI, что имеет значение для начала игры). В реальной игре мы бы, возможно, захотели спросить его или ее, “уверены” ли они, что хотят прервать игру в случае “пересдачи” карт, но для этого домашнего задания мы предполагаем, что он или она знают, что они делают, когда нажимают на эту кнопку.

Перетащите новую кнопку на storyboard. Заметьте, что голубой цвет надписи как для кнопки, так и для метки, на зеленом фоне плохо различимы. Вы можете поменять цвет всех надписей на кнопках с помощью Tint Color на всей storyboard, если на  закладке File Inspector (Файловый инспектор) укажите Global Tint:

Глобальное изменение цвета надписей на storyboard

Дайте кнопке название ‘Deal». Для того, чтобы эта кнопка функционировала,  создайте action (путем Ctrl-перетягивания в Ассистенте Редактора от кнопки к файлу CardGameViewController.m), который установит игру Matchismo заново и адаптирует пользовательский интерфейс:

[objc highlight=»5,6″]
// CardGameViewController.m
// Assignment-2-iOS7
. . . . . . . . . . .
— (IBAction)dealPress:(id)sender {
self.game = nil;
[self updateUI];
}
. . . . . . . . . . .
[/objc]

Полный код для этого пункта доступен на Github.

Пункт 3

Перетяните переключатель (UISwitch) или элемент сегментного управления (UISegmentedControl) куда-нибудь на ваш View, чтобы управлять количеством “совпадающих” карт в игре: 2 карты или 3 карты (то есть переключение между режимами “2-х карточное совпадение” и  “3-х карточное совпадение”). Дайте игроку подходящее количество очков в зависимости от трудности достижения совпадения карт. В 3-х карточном режиме совпадения возможно получение некоторого количество очков (хотя и значительно меньшее) при выборе 3-х карт и совпадении только двух из них. В этом случае все 3 карты должны быть выведены из игры (хотя совпали только 2 карты).  В 3-х карточном режиме совпадения выбор 2-х карт никогда не рассматривается как совпадение.

Перетащите элемент сегментного управления на storyboard и измените названия его сегментов:

Assignment 2 task 2

 

Подсоединим его ( Ctrl-перетаскиванием ) как  к outlet property

[objc highlight=»1″]
@property (weak, nonatomic) IBOutlet UISegmentedControl *numberOfMatchesSegment;

[/objc]

так и к action

[objc]
— (IBAction)changeNumberOfMatches {
self.game =nil;
[self updateUI];
}
[/objc]

. Если сегментный элемент меняет свое значение, то вызывается этот action. Понятно, что если вы меняете количество карт, которые должны «совпадать», то есть условия игры, то игра должна начаться сначала. Поэтому мы выполняем те же действия, что и при пересдаче карт. Новое значение должно быть считано при «отложенной» инициализации самой игры в нашем CardGameViewController. Но также как со свойством deck, считывание значения сегментного элемента управления мы произведем во вспомогательном методе ( в дальнейшем нам это пригодится для объектно-ориентированного варианта CardGameViewController) :

[objc highlight=»8,15″]
. . . . . .
@implementation CardGameViewController

— (CardMatchingGame *)game
{
if (!_game) _game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]];
_game.numberOfMatches =[self numberOfMatches];

return _game;
}

— (NSUInteger)numberOfMatches
{
return self.numberOfMatchesSegment.selectedSegmentIndex+2;
}

. . . .
[/objc]

Но для этого необходимо в игру (в public API нашего класса CardMatchingGame) добавить свойство, которое позволяет задавать количество «совпадающих» карт в игре:

[objc highlight=»9″]
@interface CardMatchingGame : NSObject

-(instancetype)initWithCardCount:(NSUInteger)count
usingDeck:(Deck *)deck;

-(void)chooseCardAtIndex:(NSUInteger)index;
-(Card *)cardAtIndex:(NSUInteger)index;

@property (nonatomic) NSUInteger numberOfMatches;
@property (nonatomic,readonly) NSInteger score;

@end
[/objc]

Так как количество «совпадающих» карт в игре не может быть менее 2-х, то в файл имплиментации нашей игры (CardMatchingGame.m) поместим setter для нашего нового свойства numberOfMatches:

[objc]
— (void)setNumberOfMatches:(NSUInteger)numberOfMatches
{
_numberOfMatches = numberOfMatches >= 2 ? numberOfMatches :2;
}
[/objc]

Теперь надо модифицировать алгоритм «совпадения»

[objc]
-(int)match:(NSArray *) otherCards

[/objc]

в классе PlayingCard на случай совпадения n карт. По идеи в случае n карт нет необходимости выделять карту, для которой вызывается этот метод, так как нам потребуется сравнение каждой с каждой из всех имеющихся карт, включая ту, для которой вызывается этот метод. Поэтому мы будем предполагать, что массив otherCards содержит все «выбранные» (isChoosen = True), но не «совпавшие» ранее карты (isMatched = False). Каждая карта должна внести хотя бы одно совпадение, и если общее число совпадений не менее numberOfMatches-1, то считаем, что numberOfMatches карт «совпали». Естественно, «совпадение» по масти оценивается в 4 очка, а «совпадение» по рангу в 1 очка. Затем подсчитываем общий счет от всех совпадений.

[objc]
-(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;
}
[/objc]

Естественно нужно правильно накапливать «выбранные», но «несовпавшие» карты при выборе очередной карты в методе chooseCardAtIndex: в классе нашей игры (CardMatchingGame.m). Для этого создадим в расширении класса CardMatchingGame новое свойство — массив «выбранных», но «несовпавших» ранее карт:

[objc highlight=»7″]
#import "CardMatchingGame.h"

@interface CardMatchingGame()

@property (nonatomic, readwrite) NSInteger score;
@property (nonatomic, strong) NSMutableArray *cards; // of Card
@property (nonatomic,strong) NSMutableArray *faceUpCards; // of Card

@end
. . . . . . . . . . . . .
[/objc]

, который будем использовать для анализа на «совпадение»:

[objc highlight=»16,19,21,22,25″]
@implementation CardMatchingGame

. . . . . . . . . . . . . .
static const int MISMATCH_PENALTY = 2;
static const int MATCH_BONUS = 4;
static const int COST_TO_CHOOSE = 1;

-(void)chooseCardAtIndex:(NSUInteger)index
{
Card *card = [self cardAtIndex:index];
if (!card.isMatched) {
if (card.isChosen) {
card.chosen =NO;
} else {
// put choosen card in array self.faceUpCards
self.faceUpCards= [[NSMutableArray alloc] initWithArray:@[card]];
for (Card *otherCard in self.cards) {
if (otherCard.isChosen && !otherCard.isMatched) {
[self.faceUpCards insertObject:otherCard atIndex:0];
//—————- decision on match
if ([self.faceUpCards count] == (self.numberOfMatches)) {
int matchScore = [card match:self.faceUpCards];
if (matchScore) {
self.score += matchScore * MATCH_BONUS;
for (Card *faceUpCard in self.faceUpCards) {
faceUpCard.matched =YES;
}

} else {
self.score -= MISMATCH_PENALTY;
for (Card *faceUpCard in self.faceUpCards) {
if (faceUpCard != card) faceUpCard.chosen =NO;
}
}
break;
} //————— end of decision on match
}
}
card.chosen = YES;
self.score -= COST_TO_CHOOSE;
}
}
}
. . . . . . . . . . . . . . . .
[/objc]

Предложение, высказанное в подсказке 7 выполнено, поскольку, если мы имеем совпадение 2-х карт при 3-х карточной игре, то при совпадении ранга нам будет начисляться 1 очко, а при совпадении 3-х карт с одинаковым рангом — 2 очка.

Полный код для этого пункта доступен на Github.

Пункт 4

Сделайте неработоспособным (disable) элемент управления режимом “2-х карточное совпадение” / “3-х карточное совпадение” (то есть  переключатель UISwitch или UISegmentedControl в обязательном задании 3), когда игра началась (то есть после первого переворота карты в игре) и вновь работоспособным (re-enable), когда происходит “пересдача” карт. (то есть кнопка Deal нажата).

Делаем неработоспособным элемент сегментного управления при выборе карты и восстанавливаем его работоспособность при нажатии кнопки «Deal» для «пересдачи» :

[objc highlight=»5,11″]
@implementation CardGameViewController
. . . . . . . . . . . . .
— (IBAction)touchCardButton:(id)sender
{
self.numberOfMatchesSegment.enabled =NO;
NSUInteger cardIndex = [self.cardButtons indexOfObject:sender];
[self.game chooseCardAtIndex:cardIndex];
[self updateUI];
}
— (IBAction)dealPress:(id)sender {
self.numberOfMatchesSegment.enabled =YES;
self.game = nil;
[self updateUI];
}
. . . . . . . . . . . . .
[/objc]

Полный код для этого пункта доступен на Github.

Пункт 5

Добавьте куда-нибудь на экран текстовую метку, описывающую результат последнего рассмотрения CardMatchingGame карты, выбранной игроком. Например: “Matched J♥️ J♠️ for 4 points.”  или “6♦️ J♣️ don’t match! 2 point penalty!” или “8♦️”, если выбрана только одна карта или просто “пробел”, если вообще никаких карт не выбрано. Не нарушайте правил MVC и не создавайте UI в вашей Model. Под “UI” подразумевается все, что вы собираетесь представить пользователю. Это должно работать правильно в любом из режимов, упомянутых в обязательном задании 3.

Давайте поговорим немного о том, как нужно представить результаты анализа на совпадения и где их формировать. Как следует из Пункт 5, результат последнего переворота карты должен быть представлен пользователю (то есть это View ) описаниями (descriptions) карт, участвующих в анализе «совпадения». Эти описания карт  разделены пробелами, и  их должна сопровождать информация о количестве очков, полученных игроком при последнем «перевороте» карты и о выигрыше или проигрыше. Но View ничего не знает о том, какие карты анализировались, и тем более ему ничего не известно о «штрафах» и «бонусах» при «совпадении» или «несовпадении» карт. Этой информацией располагает только  Model, но View не может извлекать информацию из Model. Поэтому API Model должен быть таким, чтобы после каждого «переворота» карты (метод chooseCardAtIndex:) , Controller мог иметь доступ к массиву карт, исследуемых на совпадение, и к количеству очков, назначенных в зависимости от результатов «совпадения». Поэтому добавим в класса CardMatchingGame.h следующие свойства:

[objc highlight=»3,4,7″]
//CardMatchingGame.h
……….
@property (strong,nonatomic) NSArray *matchedCards;
@property (readonly,nonatomic) NSInteger lastFlipPoints;

//CardMatchingGame.m
@property (readwrite,nonatomic) NSInteger lastFlipPoints;
…….
[/objc]

Карты, исследуемые на «совпадение», уже собираются в массиве

[objc highlight=»3″]
//CardMatchingGame.m
…….
@property (nonatomic,strong) NSMutableArray *faceUpCards; // of Card
…….
[/objc]

Когда вы выбираете карту, в Model  (класс CardMatchingGame) срабатывает метод  chooseCardAtIndex: и текущая карта, если она ранее не «совпадала»  и была повернута «лицом» вниз, добавляется в массив self.faceUpCards, который исследуется методом match: на предмет «совпадения» карт. Все это уже есть в нашем алгоритме, и нам остается только скопировать «внутренний» массив self.faceUpCards в массив self.matchedCards, который и предъявляется API для внешнего использования.Кроме того, вместо установки значения self.score напрямую, мы будем использовать новое свойство self.lastFlipPoints,  и с помощью его будем в конце подсчитывать окончательный счет.
В результате получим следующий код метода chooseCardAtIndex:

[objc highlight=»13,23,29,34,40,41″]
static const int MISMATCH_PENALTY = 2;
static const int MATCH_BONUS = 4;
static const int COST_TO_CHOOSE = 1;

-(void)chooseCardAtIndex:(NSUInteger)index
{
Card *card = [self cardAtIndex:index];
if (!card.isMatched) {
if (card.isChosen) {
card.chosen =NO;
} else {
// match against another cards
self.faceUpCards= [[NSMutableArray alloc] initWithArray:@[card]];
self.lastFlipPoints = 0;
for (Card *otherCard in self.cards) {
if (otherCard.isChosen && !otherCard.isMatched) {
[self.faceUpCards insertObject:otherCard atIndex:0];
// decision on match
if ([self.faceUpCards count] == (self.numberOfMatches)) {

int matchScore = [card match:self.faceUpCards];
if (matchScore) {
self.lastFlipPoints = matchScore * MATCH_BONUS;
for (Card *faceUpCard in self.faceUpCards) {
faceUpCard.matched =YES;
}

} else {
self.lastFlipPoints = — MISMATCH_PENALTY;
for (Card *faceUpCard in self.faceUpCards) {
if (faceUpCard != card) faceUpCard.chosen =NO;
}
}
self.matchedCards =[self.faceUpCards copy];
break;
}
// decision on match
}
}
self.score+= self.lastFlipPoints — COST_TO_CHOOSE;
self.matchedCards =[self.faceUpCards copy];
card.chosen = YES;
}
}
}
 
[/objc]

Screen Shot 2014-08-25 at 5.11.12 PM

Добавляем метку на наш storyboard. Так как нам необходимо, чтобы метка ничего не показывала в начале игры, то мы вынуждены убрать какой-либо текст с в этой метке (как показано на рисунке). Это сделает нашу метку невидимой и может в дальнейшем создать некоторые проблемы. Если вас это напрягает, то вы можете там разместить какой-то текст, а затем, например, в  viewDidLoad, его убрать. Мы этого делать не будем.

Создадим outlet к этой метке:

[objc]

@property (weak, nonatomic) IBOutlet UILabel *resultsLabel;

[/objc]

Метка будет устанавливаться всякий раз при модификации пользовательского интерфейса ( срабатывания метода updateUI ) на основе новых public  свойств нашей игры, то есть класса CardMatchingGame. Мы  создаем строку с описаниями «выбранных» карт, разделенными пробелом, и в зависимости от количества насчитанных очков размещаем дополнительный текст. Таким образом мы добавляем в метод updateUI вызов метода  updateFlipResult:

[objc]
— (void)updateUI
{
…….
[self updateFlipResult];
}

[/objc]

А метод updateFlipResult будет работать на основе новых свойств нашей игры self.game — self.game.machedCards и self.game.lastFlipPoints:

[objc highlight=»4,7,8,10,13,17, 26″]
-(void)updateFlipResult
{
NSString *text=@" ";
if ([self.game.matchedCards count]>0)
{
text = [ text stringByAppendingString:
[self.game.matchedCards componentsJoinedByString:@" "]];
if ([self.game.matchedCards count] == [self numberOfMatches])
{
if (self.game.lastFlipPoints<0) {
text = [ text stringByAppendingString:
[NSString stringWithFormat:@"✘ %ld penalty",
(long)self.game.lastFlipPoints]];
} else {
text = [ text stringByAppendingString:
[NSString stringWithFormat:@"✔ +%ld bonus",
(long)self.game.lastFlipPoints]];
}
} else text =[self textForSingleCard];
} else text = @"Play game!";
self.resultsLabel.text = text;
}

— (NSString *)textForSingleCard
{
Card *card = [self.game.matchedCards lastObject];
return [NSString stringWithFormat:@" %@ flipped %@",card,(card.isChosen) ? @"up!" : @"back!"];
}
[/objc]

Полный код для этого пункта доступен на Github.

Пункт 6

Измените пользовательский интерфейс (UI) вашей игры так, чтобы у вас было 30 карт вместо 12. Посмотрите подсказки относительно размера карт.

Измените размер карт — вместо применения подсказки, вы можете использовать Инспектор Размера ( Size Inspector) в Области Утилит для установки нужного размера ( на рис. ниже 40х60 ) напрямую. Кроме того, для размножения строк из 6-ти карт можно использовать меню Edit -> Duplicate, а также Copy — Paste. Не забудьте подсоединить ваши карты к outlet

[objc]

@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;

[/objc]

в вашем  viewController.

Assignment 2 Task 6

Измените фонт текста на кнопках на System 14.0
Полный код для этого пункта доступен на Github.

Пункт 7

Не изменяйте сигнатуру любых public методов (Сигнатура метода — это сокращенная форма записи параметров метода и типов возвращаемого значения. Следует подчеркнуть, что в сигнатуру не входят ни имя метода, ни имена параметров) , которые мы создали на лекции. Допустимо изменять сигнатуру private методов (хотя вряд ли вам это понадобится) или добавлять public и/или private методы.

… Выполнено.

Пункт 8 ( дополнительный 1 )

Добавить ползунок UISlider к UI, который обеспечивает путешествие по истории результатов выбора карты в текущей карточной игре и отображение их пользователю (передвижение ползунка UISlider будет модифицировать содержание текстовой метки, которую вы создали в Обязательном задании 5 для показа состояния игры). Когда отображаются прошлые результаты, возможно, вы захотите  чтобы тексовая метка была серой или еще чем-то (обратите внимание на на метод alpha класса UIView и заметьте, что UISlider наследует от  UIView) так, чтобы было понятно, что это “прошлое”. И каждый раз, когда происходит новый выбор карты, вы возможно захотите “прыгнуть в настоящее” на слайде. Выполнение этого дополнительного задания потребует от вас знакомства с API класса UISlider и добавления структуры данных в ваш Controller для отслеживания истории. Это может быть выполнение менее чем дюжиной  (12) строк кода.

История предыдущих переворотов карт ничего не вносит в нашу модель игры, следовательно мы будем запоминать историю «переворотов» в нашем  viewController.  Для этого создадим массив и его «отложенное» получение  экземпляра класса (lazy instantiation)  :

[objc]
@interface CardGameViewController ()
……
@property (strong,nonatomic) NSMutableArray *flipsHistory;
……
— (NSMutableArray *)flipsHistory
{
if (!_flipsHistory)_flipsHistory = [[NSMutableArray alloc] init];
return _flipsHistory;
}

[/objc]

«Запись» истории будет происходить в методе updateFlipResult, в котором формируется информационная строка для пользователя о результатах «переворота» очередной карты:

[objc highlight=»4″]
-(void)updateFlipResult
{
……….
[self.flipsHistory addObject:text];
} else text = @"Play game!";
self.resultsLabel.text = text;
}

[/objc]

Когда вы нажимаете на кнопку «Deal» пересдачи карт, то массив истории «переворотов» устанавливается в nil:

[objc highlight=»4″]
— (IBAction)Deal:(UIButton *)sender
{
……..
self.flipsHistory =nil;
[self updateUI];
}

[/objc]

Добавьте ползунок (slider) на ваш storyboard, установите его диапазон в 0 (потому что в начале у нас нет истории) :

Assignment 2 Task 8

 

Подсоедините ползунок к outlet property и к  action.

[objc highlight=»3,6″]
@interface CardGameViewController ()
…………..
@property (weak, nonatomic) IBOutlet UISlider *historySlider;
@end
……
— (IBAction)takeHistory:(UISlider *)sender {

}
…………
[/objc]

Assignment 2 Task 8

Assignment 2 Task 8

По умолчанию ползунок работает с аналоговым значением, то есть получает значения  (values) ввиде чисел с плавающей точкой. Но у нас дискретная история, поэтому мы будем двигать ползунок к ближайшей доступной дискретной точке нашей истории. Если вы передвигаете ползунок с «текущей» позиции, то нужно поменять значение  alpha метки с отображением информации о прошлом «перевороте» чтобы сделать метку более «прозрачной» (translucent), и конечно нужно заменить текст этой метки на информацию о результатах этого  прошлого «переворота». Немного отвлечемся от ползунка и подумаем о том, как обозначить «текущую» позицию и дискретные метки в нашей «истории». Так как у нас «история» «переворотов» карт, то естественно использовать для этого номер «переворота».Добавим дополнительное свойства 

[objc highlight=»3″]
@interface CardGameViewController ()
………
@property (nonatomic) int flipCount;
…….
@end
[/objc]

с помощью которого будем считать количество «переворотов» в нашей игре. Для логики игры это не имеет значения, поэтому разместим это в нашем viewController. Количество «переворотов» соответствует количеству нажатий кнопок карт или количеству срабатываний метода  touchCardButton:, поэтому там и будем рассчитывать количество переворотов:

[objc highlight=»6″]
— (IBAction)touchCardButton:(id)sender
{
self.numberOfMatchesSegment.enabled =NO;
NSUInteger cardIndex = [self.cardButtons indexOfObject:sender];
[self.game chooseCardAtIndex:cardIndex];
self.flipCount++;
[self updateUI];
}
[/objc]

Количество «переворотов» следует выставить в 0 при пересдачи карт

[objc highlight=»3″]
— (IBAction)dealPress:(id)sender {
……..
self.flipCount =0;
……..
}

[/objc]

Количество «переворотов» будем показывать пользователю на UI в маленькой метке

[objc highlight=»3″]
@interface CardGameViewController ()
………
@property (weak, nonatomic) IBOutlet UILabel *sliderMaxLabel;
…….
@end
[/objc]

, расположенной справа от ползунка

Assignment 2 Task 8
Вернемся к нашему ползунку. Для того, чтобы ползунок начал действовать мы должны выставлять его максимальное значение, которое уместно выставлять при модификации экрана, то есть в методе updateUI. Это и будет «текущее» значение числа «переворотов». Одновременно поместим «текущее» значение в метке справа от ползунка:

[objc highlight=»4,5,6″]
— (void)updateUI
{
………
self.historySlider.maximumValue = self.flipCount;
[self.historySlider setValue:(float)self.flipCount animated:YES];
self.sliderMaxLabel.text = [NSString stringWithFormat:@"%d",(int)ceilf(self.historySlider.maximumValue)];
}

[/objc]

При перемещении ползунка работает action метод takeHistory:

[objc highlight=»1″]
— (IBAction)takeHistory:(UISlider *)sender {
int selectedIndex = (int) sender.value;
if (selectedIndex <0 || (selectedIndex > self.flipCount-1)) return;
self.resultsLabel.alpha = (selectedIndex < self.flipCount-1 ) ? 0.5 : 1.0;
NSString *text = [NSString stringWithFormat:@"%d:",(selectedIndex+1)];
self.resultsLabel.text = [text stringByAppendingString:[self.flipsHistory objectAtIndex:selectedIndex]];
}

[/objc]

Полный код для этого пункта доступен на Github.

Пункт 9 ( дополнительный 2 )

Поменяйте изображение обратной стороны карты (то есть что-то отличное от логотипа Stanford). А также установите иконки с изображениями для самого приложения Matchismo и для его запуска. Попытайтесь установить правильное разрешение для изображения каждой иконки: Application и Launch. Отпустите на свободу свое воображение при конструировании этих иконок.

Используя свою любимую графическую программу, создайте изображение обраьной стороны карты, и экспортируйте его в двух форматах  40×60 и 80×120. В Xcode в навигаторе выберите папку  image assets и перетяните туда свои новые изображения обратной стороны карты:

Для иконки вашего приложения необходимо 3 различных размера  2*29pt = 58×58, 2*40pt = 80×80 and 2*60pt = 120×120. Также как и раньше их необходимо перетянуть в ваш  image assets:

App icon

Для получения launch images запустите свое приложение на iPhone simulator (симуляторе iPhone), один раз на 3.5-дюймовом и оди раз на  4-дюймовом и сделайте screen shots (cmd-s). The launch images должны быть идентичны первому экрану приложения, за исключением текста и элементов, которые могут изменяться. Откройте полученные  screen shots и уберите все тексты и все изменяемые элементы. Для Matchismo убираем метку счета, кнопку пересдачи карт, переключатель для количества карт, анализируемых на «совпадение» ( 2 карты/ 3 карты) и все, что находится на  the status bar в верхней части экрана:

iOS Simulator Screen shot Aug 26, 2014, 2.40.09 PM

Теперь у вас должно быть два launch images размером  640×960 and 640×1136, которые вы можете перетянуть в ваш  image assets:


Screen Shot 2014-08-26 at 2.59.17 PM

 

The complete code is available on Github.

Stanford CS 193P iOS 7 — Домашнее задание 2: 6 комментариев

  1. Здравствуйте, зачем вы объявили свойство «description» в классе Card? Оно ведь наследуется от NSObject.
    «@interface Card : NSObject

    @property (nonatomic,weak)NSString *description;»

    • У меня в коде Card.m это выглядит НЕ ТАК:

      -(NSString *)description
      {
      return self.contents;
      }
      Это не объявление переменной, а отложенное получение экземпляра класса ( lazy instantiation).
      Этот код срабатывает не при инициализации, а позже — при обращении из кода.
      При этом в качестве description берется self.contents, которое зависит от типа карты (игральная карта или карта SET)и переопределяется в subclass класса Card.
      Он используется для удобства получения текстового NSString представления карты при формировании строки результата
      text = [text stringByAppendingString:[self.game.matchedCards componentsJoinedByString:@» «]];
      В результате вы на экране видите строку c описанием игральных карт, например:
      9♠️ 5♦️ или 6♥️ 3♣️

      • Прошу прощения за то, что не достаточно пояснил вопрос. К написанному в Card.m вопросов нет. Однако, в классе Card.h объявлено @property (nonatomic, weak) NSString *description, которое уже наследуется от NSObject.
        В коде со всеми выполненными пунктами вы убрали это свойство, однако в версии кода после пункта 5 (Assignment 2 task 5 на git), свойство осталось (xcode выдает warning).

        • Вполне возможно. Код писался давно, тогда еще не было description, приходилось декларировать в .h.
          Сейчас в окончательной версии https://github.com/BestKora/Assignment-2-iOS7 его нет. Поправить в tags на Github по-моему трудно, хотя и возможно. Постараюсь это сделать. Спасибо.

          • Если я хочу переопределить свойство, которое наследуется, мне не нужно его объявлять, правильно? Но если я его всё-таки объявлю, это ни на что не повлияет?

          • Вы не должны объявлять наследуемое свойство, которое переопределяете. Если вы его объявляете, то это будет другое свойство, и вам об этом сообщают в предупреждении :
            Вам говорят, что наследуемое свойство «readwrite» (то есть и setter и getter), ваше свойство будет только readonly (только getter).
            Но все будет работать.

Обсуждение закрыто.