Task1

Follow the detailed instructions in the lecture slides (separate document) to build and run Matchismo in the iPhone Simulator in Xcode 5. Do not proceed to the next steps unless your card flips back and forth as expected and builds without warnings or errors.

跟着视屏Demo做即可,白胡子爷爷讲得超级详细,有疑问的地方在Lecture#2的Slide里面基本都可以找到答案。

Task2

Be sure to include all the code for the four classes shown in the first two lectures: Card, Deck, PlayingCard and PlayingCardDeck. You must type this code in (e.g., do not copy and paste it from somewhere). The whole point of this Required Task is to gain experience editing code in Xcode and to get used to Objective-C syntax.

把Lecture#1,2里面涉及到的四个类Card, Deck, PlayingCard, PlayingCardDeck敲进去,一定要自己敲进去,因为通常情况是,一边敲一边问题就来了。

Task3

Add a private property of type Deck * to the CardGameViewController.

从这里开始就是作业要求独立完成的部分了,题目说得很清楚字儿也极短,要敲的代码也很少,但是这里有些需要理解的部分。

首先还是在XxxViewController.m里声明一个Deck属性,别忘了先import在Task2里写好的PlayingCard.h。

@property (strong,nonatomic) Deck *myDeck;

我们声明这个Deck私有属性的目的是为了初始化一张有52张牌的桌子(好别扭的说法==),但是其实我们可以看到我们之前写的init的方法是在PlayingDeck里面的。这里白胡子大叔还有Hint针对这个问题呢。

Even though the type of the property you must add is required to be a Deck (not PlayingCardDeck) you’ll obviously have to lazily instantiate it using a PlayingCardDeck. This is perfectly legal in object-oriented programming because a PlayingCardDeck inherits from Deck and thus it “is a” Deck. If you are confused by this concept in object-oriented programming, this course may be rather difficult for you.

好吧,我不会承认我最开始就是自作聪明地声明的PlayingCard的╮(╯▽╰)╭,结果被赤裸裸地鄙视了==其实这个呢,我们做完Task4就可以看出一些端倪了。

Task4

Use lazy instantiation to allocate and initialize this property (in the property’s getter)

so that it starts off with a full deck of PlayingCards.

添加代码如下:

-(Deck *)myDeck
{
    if (!_myDeck) {
     _myDeck = [[PlayingDeck alloc]init];
    }
return _myDeck;
}

首先这里用到了传说中lazy instantiation(惰性初始化),我们之前在Deck.m中初始化Card的时候就用到了这个;其次,这里的_myDeck是用PlayingDeck初始化的!所以之前担心的Deck里面没有override的init方法是白费功夫了。不过为啥这么写,跟小伙伴讨论的结果是用了多态

Task5

Matchismo so far only displays the A♣ over and over. Fix Matchismo so that each time the card is flipped face up, it displays a different random card drawn from the
Deck property you’ve created above. In other words, Matchismo should flip through an entire deck of playing cards in random order, showing each card one at a time.

这个问题也不难,通过前面的任务,我们已经有了“一张有52张牌的桌子”,而在Deck.h中白胡子大叔已经带我们实现了一个drawRandomCard的函数,那么只要在每次按下button的时候,我们调用一下这个函数就实现了从牌堆里随机取出一张牌(注意已经取出了),然后显示card的内容直接用contents这个函数。

  • (IBAction)touchCardButton:(UIButton *)sender {
    ……
    else{

    Card *myCard = [self.myDeck drawRandomCard];

    ……

    [sender setTitle:[myCard contents] forState:UIControlStateNormal];

    }

    self.flipCount++;//both call setter and getter

    }

这个Task主要的问题在于,这下子初始的情况要显示牌的背面。这个不是能在touchCardButton里实现,因为我们的touchCardButton监听的一个action,这里需要的是一个outlet。

首先,按住ctrl再把button拖入@implemention中,在connection里面选outlet,随便输入一个名字比如cardButton,这样我们又声明了一个property;然后在之前白胡子大叔“忽悠”我们删掉的函数viewDidLoad中添加下面的代码:

-(void)viewDidLoad{
    [super viewDidLoad];
    [self touchCardButton:self.cardButton];
    self.flipCount = 0;
}

前两句代码我也不是特别明白。对于第一句,其实在PlayingDeck里面实现init的时候我们也写过类似的:

self = [super init]

个人感觉是一个道理,在那里白胡子大叔解释很多:

This is the ONLY time you would ever assign something to self

But we have to check to see if our superclass can initialize itself. The assignment to self is a bit of protection against our trying to continue to initialize ourselves if our superclass couldn’t initialize. Just always do this and don’t worry about it too much.
Sending a message to super is how we send a message to ourselves, but use our superclass’s implementation instead of our own.
Standard object-oriented stuff.

然后只要先在button上随便写点东西,根据我们的touchCardButton函数,就会进入到else里面显示card的背面。

Task6

Do not break the existing functionality in Matchismo (e.g. the Flips counter should still continue to count flips).

这没什么好说的了╮(╯▽╰)╭

补充

在Lesson3里,白胡子大叔有讲这次作业的答案,不过他说这是唯一一次讲答案了╮(╯▽╰)╭
他主要优化了三个地方,一个是之前提到的,初始时显示牌的背面,第二个是没有牌的时候,要提示用户没牌了,第三个是设置的flipCount一直计数也是非常讨厌的。

  • 优化1:
    大叔做的没那么复杂,就是把字符串删掉,背景设置成了cardback即可。看来我想多了,不过这也的确不符合task6的精神

  • 优化2:
    之前的写的代码在没牌的时候会用下面的代码把button都移除了,点到最后突然没了还是怪瘆人的,主要万一还想干点别的也没法做了,于是还是设置成牌翻完了就一直显示为背面好了。

    [sender removeFromSuperview];
  • 优化3:
    这个也很简单哇,把self.flipCount++移到if(myCard)里面即可。

    -(IBAction)touchCardButton:(UIButton *)sender {
    if ([sender.currentTitle length]) {
    [sender setBackgroundImage:[UIImage imageNamed:@"cardback"]  forState:UIControlStateNormal];
    [sender setTitle:@"" forState:UIControlStateNormal];
    }
    else{
    Card *myCard = [self.myDeck drawRandomCard];
    if (myCard) {
        [sender setBackgroundImage:[UIImage imageNamed:@"cardfront"] forState:UIControlStateNormal];
        [sender setTitle:[myCard contents] forState:UIControlStateNormal];
        self.flipCount++;//both call setter and getter
    }
    /*
    else{
         [sender removeFromSuperview];
    }*/
    } 
    }

效果图

完整代码

图片描述

初学者理解不当之处,求指正,谢谢。

-EOF-


严肃的马甲
110 声望10 粉丝