转载

(6/18)重学Standford_iOS7开发_控制器多态性、导航控制器、选项卡栏控制器_课程笔记

终于有时间跟新了,两周时间复(yu)习(xi)了5门考试累觉不爱。。。。。。

--------------------------------------------------------------------------我是正文分割线---------------------------------------------------------------------------------------------

第六课

1、控制器多态性

这里控制器多态性是指在控制器中使用继承,通过继承构造通用视图控制器(ViewController),在具体的MVC中继承通用视图控制器实现具体功能(通常是父类中的抽象方法)

注意:objective-C并没有abstract关键字,因此通常要通过注释的方式说明,抽象方法需要在.h文件中声明,一般实现文件中的抽象方法没有具体功能,只返回nil。

storyboard的多态控制器类需要在属性检查器中更改指定的类。

说明:通过继承,父类中的连接也会被完整的继承下来,如输出口(Outlet)等。

demo地址: https://github.com/NSLogMeng/Stanford_iOS7_Study/commit/238167d6080df94e0383aceccab6d16fea290af2

2、多MVC

理解:多MVC基本组合方式,子MVC作为父MVC的视图(View)呈现。

(6/18)重学Standford_iOS7开发_控制器多态性、导航控制器、选项卡栏控制器_课程笔记

(1)UINavigationController(以日历应用为例)

1) Navigation Bar

a.title 当前MVC的标题,与内容有关

b.navigationItem.rightBarButtonItems(ViewController属性)  功能按钮(注意与UIButton区别,UIBarButton的数组,即可以嵌入多个BarButton)

c.Back Button (一般由UINavigationController自动设定,默认为上一MVC的title,长度较长时显示为back,亦可自行设置)

当点击当前MVC的back时,当前MVC会被释放,返回前一个MVC

2)toolbarItems (ViewController属性)

底部出现(an NSArray of UIBarButtonItems )

3)rootViewController

指向一个MVC的controller,作为根MVC

//UINavigationController常用方法  - (instancetype)initWithRootViewController:(UIViewController *)rootViewController; // 设置根视图控制器 - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; //压入新视图 - (UIViewController *)popViewControllerAnimated:(BOOL)animated;//弹出当前视图 //navigationBar @property(nonatomic,getter=isNavigationBarHidden) BOOL navigationBarHidden; - (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated;  //toolbar @property(nonatomic,getter=isToolbarHidden) BOOL toolbarHidden NS_AVAILABLE_IOS(3_0); - (void)setToolbarHidden:(BOOL)hidden animated:(BOOL)animated NS_AVAILABLE_IOS(3_0);

(2)segue(非常重要,MVC间切换的基础)

push:一种以push,pop方式在UINavigationController上进行MVC切换的方式

identifier:用来表示push,方便在代码中使用

 1 //需要跳转时的Action  2 - (IBAction)rentEquipment  3 {  4     if (self.snowTraversingTalent == Skiing) {  5         [self performSegueWithIdentifier:@“AskAboutSkis” sender:self];  6     } else {  7         [self performSegueWithIdentifier:@“AskAboutSnowboard” sender:self];  8     }   9 } 10  11 //为各segue做跳转前的准备 12 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 13     if ([segue.identifier isEqualToString:@“DoSomething”]) { 14         if ([segue.destinationViewController isKindOfClass:[DoSomethingVC class]])         { 15             DoSomethingVC *doVC = (DoSomethingVC *)segue.destinationViewController; 16 doVC.neededInfo = ...; } 17 }  18 } 19  20 //segue跳转判断 21 - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender { 22if ([segue.identifier isEqualToString:@“DoAParticularThing”]) { 23         return [self canDoAParticularThing] ? YES : NO; 24     } 25 }

注意:segue跳转的准备工作中并未设置好输出口,因此不能直接赋值给新MVC的输出口,MVC中需要设置API来接收数据。

(3)demo : Attributor Stats

Use a UINavigationController to show “statistics” on colors and outlining in Attributor.

demo地址: https://github.com/NSLogMeng/Stanford_iOS7_Study/commit/fba52faaefd72cb1d4e0ef0c9029092fd5749f63

(4)UITabBarController(以时钟应用为例)

//设置选项个数,一般不超过5个,超过5个时,第五个及其他将集合在最后一栏 @property (nonatomic, strong) NSArray *viewControllers;

3、作业

要求:a.在Matchismo 的基础上添加一个新的MVC作为Set纸牌匹配游戏。

b.新的Set纸牌游戏与原纸牌游戏类似(3张牌匹配模式)

c.原游戏的功能保留,可以适当删减或更改UI(原要求是取消UISegmentedControll)

d.使用UITabBarController将两个游戏分开

e.Set纸牌的数目可能会与之前不同,选择合适的UI尺寸

f.使用▲ ● ■  以及NSAttributedString 来表示Set纸牌的三个属性(形状,颜色,阴影)

g.每个游戏必须展示当前的得分状况,并允许用户重置

h.使用NSAttributedString来记录并展示纸牌匹配状况

i.使用UINavigationController 添加新的MVC来详细展示纸牌匹配情况的历史

Set纸牌游戏规则: Wikipedia 或  百度百科

作业解析:

最终实现效果如下图:

(6/18)重学Standford_iOS7开发_控制器多态性、导航控制器、选项卡栏控制器_课程笔记 (6/18)重学Standford_iOS7开发_控制器多态性、导航控制器、选项卡栏控制器_课程笔记 (6/18)重学Standford_iOS7开发_控制器多态性、导航控制器、选项卡栏控制器_课程笔记

实现过程:

针对上次作业的结果对纸牌游戏再次进行抽象化并添加Set纸牌游戏

抽象化结果: https://github.com/NSLogMeng/Stanford_iOS7_Study/commit/58afa7577cb339328819fccb5422aef27fd843ff

a.添加Set及Model的抽象化

SetCard

SetCard的匹配详见作业要求中的规则,此处判定匹配成功则score为1,否则为0(仅作为判定标记,也可以根据自己喜好自定义分数规则)

 1 #import "Card.h"  2    3 @interface SetCard : Card  4    5 @property (strong,nonatomic) NSString *suit;//  ▲ ● ■  6 @property (strong,nonatomic) NSString *color;// red green purple  7 @property (nonatomic) BOOL shading;  8   9 + (NSArray *)validSuits; 10 + (NSArray *)validColors; 11 + (NSUInteger)shadingNumber; 12  13 @end
 1 #import "SetCard.h"  2   3 @interface SetCard()  4   5 @end  6   7 @implementation SetCard  8   9 - (NSString *)contents 10 { 11     return nil; 12 } 13  14 - (int)match:(NSArray *)otherCards 15 { 16     int score = 0; 17     18     if ([otherCards count] == 2) 19     { 20         SetCard *firstCard = [otherCards firstObject]; 21         SetCard *secondCard = [otherCards lastObject]; 22         if ([self.suit isEqualToString:firstCard.suit] && [firstCard.suit isEqualToString:secondCard.suit]) 23         { 24             score += 1; 25         } 26         else if (![self.suit isEqualToString:firstCard.suit] && ![self.suit isEqualToString:secondCard.suit] && ![firstCard.suit isEqualToString:secondCard.suit]) 27         { 28             score += 1; 29         } 30         else 31         { 32             score -= 1; 33         } 34          35         if ([self.color isEqualToString:firstCard.color] && [firstCard.color isEqualToString:secondCard.color])//全相等 36         { 37             score += 1; 38         } 39         else if (![self.color isEqualToString:firstCard.color] && ![self.color isEqualToString:secondCard.color] && ![firstCard.color isEqualToString:secondCard.color])//全不相等 40         { 41             score += 1; 42         } 43         else 44         { 45             score -= 1; 46         } 47          48         if ((self.shading == firstCard.shading) && (firstCard.shading == secondCard.shading)) 49         { 50             score += 1; 51         } 52         else 53         { 54             score -= 1; 55         } 56  57     } 58      59     if (score == 3) 60     { 61         score = 1; 62     } 63     else 64     { 65         score = 0; 66     } 67      68     return score; 69 } 70  71 + (NSArray *)validSuits 72 { 73     return @[@"",@"▲▲",@"▲▲▲",@"",@"●●",@"●●●",@"",@"■ ■",@"■ ■ ■"]; 74 } 75  76 + (NSArray *)validColors 77 { 78     return @[@"red",@"green",@"purple"]; 79 } 80  81  82 + (NSUInteger)shadingNumber 83 { 84     return 2; 85 } 

SetCardDeck

与PlayingCardDeck类似

1 #import "Deck.h" 2  3 @interface SetCardDeck : Deck 4  5 @end
 1 #import "SetCardDeck.h"  2 #import "SetCard.h"  3   4 @implementation SetCardDeck  5   6 - (instancetype) init  7 {  8     self = [super init];  9     if (self) 10     { 11         for (NSString *suit in [SetCard validSuits]) 12         { 13             for (NSString *color in [SetCard validColors]) 14             { 15                     for (NSUInteger i = 0; i < [SetCard shadingNumber]; i++) 16                     { 17                         SetCard *card = [[SetCard alloc] init]; 18                         card.suit = suit; 19                         card.color = color; 20                         card.shading = (i<1) ? true : false; 21                          22                         [self addCard:card]; 23                     } 24             } 25         } 26     } 27     return self; 28 } 29  30 @end

CardMatchingGame的抽象化

上次作业中我们的CardMatchingGame只针对PlayingCard的匹配规则及方法在SetCard中任然类似或适用,因此我们抽象化CardMatchingGame,使其成为PlayingCardMatchingGame与SetCardMatchingGame的超类。

首先我们根据作业要求我们添加gameStateHistory属性来保存游戏进行的历史数据,由于可能会出现多纸牌匹配,因此添加validOfOtherCards属性来获取其余纸牌的内容。因为gameState属性最好使用只读属性,因此我们在超类中去除此属性而在子类中实现

 1 #import <Foundation/Foundation.h>  2 #import "Deck.h"  3 #import "Card.h"  4   5 static const int MISMATCH_PENALTY = 2;  6 static const int MATCH_BOUNDS = 4;  7 static const int COST_TO_CHOOSE = 1;  8   9 @interface CardMatchingGame : NSObject 10  11 // designated initializer 12 - (instancetype) initWithCardCount:(NSUInteger)count 13                          usingDeck:(Deck *)deck; 14 - (void) chooseCardAtIndex:(NSUInteger)index; 15 - (Card *) cardAtIndex:(NSUInteger)index; 16  17 - (NSString *)validOfOtherCards:(NSArray *)otherCards; 18  19 @property (nonatomic,readonly) NSString *validOfOtherCards; 20 @property (nonatomic,readonly) NSInteger score; 21 @property (nonatomic) NSUInteger gameModel;// >=2 22 @property (nonatomic,strong) NSMutableArray *gameStateHistory; 23  24 @end
  1 #import "CardMatchingGame.h"   2    3 @interface CardMatchingGame()   4    5 @property (nonatomic,readwrite) NSString *validOfOtherCards;   6 @property (nonatomic,readwrite) NSInteger score;   7 @property (nonatomic,strong) NSMutableArray *cards;//of playingcard   8    9 @end  10   11 @implementation CardMatchingGame  12   13 - (NSMutableArray *)cards  14 {  15     if (!_cards) _cards = [[NSMutableArray alloc] init];  16     return _cards;  17 }  18   19 - (NSMutableArray *)gameStateHistory  20 {  21     if (!_gameStateHistory)  22     {  23         _gameStateHistory = [[NSMutableArray alloc] init];  24     }  25     return _gameStateHistory;  26 }  27   28 - (instancetype) initWithCardCount:(NSUInteger)count usingDeck:(Deck *)deck  29 {  30     self = [super init];  31     if (self)  32     {  33         for (int i = 0; i < count; i++)  34         {  35             Card *card = [deck drawRandomCard];  36             if (card)  37             {  38                 [self.cards addObject:card];  39             }  40             else  41             {  42                 self = nil;  43                 break;  44             }  45         }  46     }  47       48     return self;  49 }  50   51 - (Card *) cardAtIndex:(NSUInteger)index  52 {  53     return (index < [self.cards count]) ? self.cards[index] : nil;  54 }  55   56 - (void) chooseCardAtIndex:(NSUInteger)index  57 {  58     Card *card = [self cardAtIndex:index];  59     if (!card.isMacthed)  60     {  61         if (card.isChosen)  62         {  63             card.chosen = NO;  64         }  65         else  66         {  67             // match against other chosen cards  68             NSMutableArray *otherCards = [NSMutableArray arrayWithCapacity:self.gameModel];  69               70             for (Card *otherCard in self.cards)  71             {  72                 if (otherCard.isChosen && !otherCard.isMacthed)  73                 {  74                     [otherCards addObject:otherCard];  75                 }  76             }  77               78             //不能放于for循环之前,否则会将本次被选择的牌加入cards,不能放于下面的if之后,否则当if成立时返回,没有将本次翻牌的cost记录,且不能翻牌  79             self.score -= COST_TO_CHOOSE * self.gameModel;  80             card.chosen = YES;  81               82             if ([otherCards count] < self.gameModel - 1)  83             {  84                 return;  85             }  86             else  87             {  88                 self.validOfOtherCards = [self validOfOtherCards:otherCards];  89                 int matchScore = [card match:otherCards];  90                   91                 if (matchScore)  92                 {  93                     self.score += matchScore * MATCH_BOUNDS * self.gameModel;  94                     for (Card *otherCard in otherCards)  95                     {  96                         otherCard.matched = YES;  97                     }  98                     card.matched = YES;  99                 } 100                 else 101                 { 102                     self.score -= MISMATCH_PENALTY * self.gameModel; 103                     for (Card *otherCard in otherCards) 104                     { 105                         otherCard.chosen = NO; 106                     } 107                 } 108             } 109         } 110     } 111 } 112  113 - (NSString *)validOfOtherCards:(NSArray *)otherCards 114 { 115     NSMutableString *string = [[NSMutableString alloc] init]; 116     for (Card *card in otherCards) 117     { 118         [string appendFormat:@"%@ ",card.contents]; 119     } 120     return string; 121 } 122  123 @end

子类化的PlayingCardMatchingGame

实现类gameState的记录,并添加方法使其在纸牌匹配时记录状态及状态历史

1 #import "CardMatchingGame.h" 2  3 @interface PlayingCardMatchingGame : CardMatchingGame 4  5 @property (nonatomic,readonly) NSString *gameState; 6  7 @end
 1 #import "PlayingCardMatchingGame.h"  2   3 @interface PlayingCardMatchingGame()  4   5 @property (nonatomic,readwrite) NSString *gameState;  6   7 @end  8   9 @implementation PlayingCardMatchingGame 10  11 - (void)chooseCardAtIndex:(NSUInteger)index 12 { 13     int forwardScore = self.score; 14     [super chooseCardAtIndex:index]; 15     [self updateGameState:forwardScore withIndexOfCard:index]; 16 } 17  18 - (void)updateGameState:(int)forwardScore withIndexOfCard:(int)index 19 { 20     if (self.score == forwardScore - self.gameModel * COST_TO_CHOOSE) 21     { 22         _gameState = [self cardAtIndex:index].contents; 23     } 24     else if (self.score < (forwardScore - self.gameModel * COST_TO_CHOOSE)) 25     { 26         _gameState = [NSString stringWithFormat:@"%@ with %@ not matched. %d point penalty!",[self cardAtIndex:index].contents,self.validOfOtherCards,forwardScore - self.score]; 27         [self.gameStateHistory addObject:_gameState]; 28     } 29     else if (self.score > (forwardScore - self.gameModel * COST_TO_CHOOSE)) 30     { 31         _gameState = [NSString stringWithFormat:@"%@ matched %@ . get %d score!",[self cardAtIndex:index].contents,self.validOfOtherCards,self.score - forwardScore]; 32         [self.gameStateHistory addObject:_gameState]; 33     } 34 } 35  36 @end

子类化的SetCardMatchingGame

与PlayingCardMatchingGame类似,注意在SetCard中并没有content的数据,因此要重写validOfOtherCard:方法,以保证正确获取SetCard的内容

1 #import "CardMatchingGame.h" 2  3 @interface SetCardMatchingGame : CardMatchingGame 4  5 @property (nonatomic,readonly) NSString *gameState; 6  7 @end
 1 #import "SetCardMatchingGame.h"  2 #import "SetCard.h"  3   4 @interface SetCardMatchingGame()  5   6 @property (nonatomic,readwrite) NSString *gameState;  7   8 @end  9  10 @implementation SetCardMatchingGame 11  12 - (void)chooseCardAtIndex:(NSUInteger)index 13 { 14     int forwardScore = self.score; 15     [super chooseCardAtIndex:index]; 16     [self updateGameState:forwardScore withIndexOfCard:index]; 17 } 18  19 - (NSString *)validOfOtherCards:(NSArray *)otherCards 20 { 21     NSMutableString *string = [[NSMutableString alloc] init]; 22     for (SetCard *card in otherCards) 23     { 24         [string appendString:card.suit]; 25     } 26     return string; 27 } 28  29 - (void)updateGameState:(int)forwardScore withIndexOfCard:(int)index 30 { 31     if (self.score == forwardScore - (self.gameModel * COST_TO_CHOOSE)) 32     { 33         _gameState = ((SetCard *)[self cardAtIndex:index]).suit; 34     } 35     else if (self.score < (forwardScore - (self.gameModel * COST_TO_CHOOSE))) 36     { 37         _gameState = [NSString stringWithFormat:@"%@ with %@ not matched. %d point penalty!",((SetCard *)[self cardAtIndex:index]).suit,self.validOfOtherCards,forwardScore - self.score]; 38         [self.gameStateHistory addObject:_gameState]; 39     } 40     else if (self.score > (forwardScore - (self.gameModel * COST_TO_CHOOSE))) 41     { 42         _gameState = [NSString stringWithFormat:@"%@ matched %@ . get %d score!",((SetCard *)[self cardAtIndex:index]).suit,self.validOfOtherCards,self.score - forwardScore]; 43         [self.gameStateHistory addObject:_gameState]; 44     } 45 } 46  47 @end

b.UI的更新

如图: (6/18)重学Standford_iOS7开发_控制器多态性、导航控制器、选项卡栏控制器_课程笔记

由于SetCard的游戏规则,SetCard并不会被翻回到背面,因此要更改纸牌的牌面图片

c.Controller的抽象化及子类化实现

上次作业中针对ViewController有很多只与PlayingCardMatchingGame有关的内容,此次将这些内容转移到PlayingCardGameViewController中,对ViewController进行抽象化,并且也在子类SetCardGameViewController中实现SetCard匹配游戏

ViewController

由于许多有关UI的操作需要在子类中实现,因此将他们放到.h文件中,同时titleForCard:与backgroundImageForCard:方法也许要在SetCard子类中重写(想想为什么?),因此也放入.h文件中

 1 #import <UIKit/UIKit.h>  2 #import "Deck.h"  3   4 @class CardMatchingGame;  5   6 @interface ViewController : UIViewController  7   8 //protected  9 //for subclasses 10 - (Deck *)createDeck;  // abstract 11  12 @property (strong,nonatomic) CardMatchingGame *game; 13 @property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons; 14 @property (weak, nonatomic) IBOutlet UILabel *scoreLable; 15  16 - (NSUInteger)cardButtonsNumber; 17 - (void) updateUI; 18 - (NSString *)titleForCard:(Card *)card; 19 - (UIImage *)backgroundImageForCard:(Card *)card; 20  21 @end
 1 #import "ViewController.h"  2 #import "CardMatchingGame.h"  3   4 @interface ViewController ()  5   6 @end  7   8 @implementation ViewController  9  10 - (NSUInteger)cardButtonsNumber 11 { 12     return [_cardButtons count]; 13 } 14  15 - (Deck *)createDeck  // abstract 16 { 17     return nil; 18 } 19  20 - (IBAction)touchCardButton:(UIButton *)sender 21 { 22     NSUInteger cardIndex = [self.cardButtons indexOfObject:sender]; 23     [self.game chooseCardAtIndex:cardIndex]; 24     [self updateUI]; 25 } 26  27 - (void) updateUI 28 { 29     for (UIButton *cardButton in self.cardButtons) 30     { 31         NSUInteger cardIndex = [self.cardButtons indexOfObject:cardButton]; 32         Card *card = [self.game cardAtIndex:cardIndex]; 33         [cardButton setTitle:[self titleForCard:card] forState:UIControlStateNormal]; 34         [cardButton setBackgroundImage:[self backgroundImageForCard:card] forState:UIControlStateNormal]; 35         cardButton.enabled = !card.isMacthed; 36     } 37     self.scoreLable.text = [NSString stringWithFormat:@"Score:%ld",(long)self.game.score]; 38 } 39  40 - (NSString *)titleForCard:(Card *)card 41 { 42     return card.isChosen ? card.contents : nil; 43 } 44  45 - (UIImage *)backgroundImageForCard:(Card *)card 46 { 47     return [UIImage imageNamed:card.isChosen ? @"cardFront" : @"cardBack"]; 48 } 49  50 @end

PlayingCardGameController

与上次作业基本相同,只不过变为从更抽象的超类中继承为子类(一些只适用于PlayingCardGame的UI与方法在本类实现),并且添加了History功能

1 #import "ViewController.h" 2  3 @interface PlayingCardGameViewController : ViewController 4  5 @end
  1 #import "PlayingCardGameViewController.h"   2 #import "PlayingCardDeck.h"   3 #import "PlayingCardMatchingGame.h"   4 #import "HistoryViewController.h"   5    6 @interface PlayingCardGameViewController ()   7    8 @property (weak, nonatomic) IBOutlet UISegmentedControl *gameModelSelectSegmented;   9 @property (weak, nonatomic) IBOutlet UILabel *gameModelLable;  10 @property (weak, nonatomic) IBOutlet UITextField *matchModelTextFiled;  11 @property (weak, nonatomic) IBOutlet UILabel *gameStateLable;  12 @property (weak, nonatomic) IBOutlet UIButton *restartButton;  13 @property (nonatomic) NSUInteger selfDefiningModel;  14   15 @end  16   17 @implementation PlayingCardGameViewController  18   19 #define CORNER_FONT_STANDARD_HIGHT 180.0  20 #define CORNER_RADIUS 12.0  21   22 //让界面变得更好看的魔法  23 - (CGFloat)cornerScaleFactor:(CGFloat)height {return height / CORNER_FONT_STANDARD_HIGHT;}  24 - (CGFloat)cornerRadius:(CGFloat)height {return CORNER_RADIUS * [self cornerScaleFactor:height];}  25   26 - (void)viewDidLoad  27 {  28     //又是个让界面变得更好看的魔法  29     self.restartButton.layer.cornerRadius = [self cornerRadius:self.restartButton.bounds.size.height];  30     self.gameModelSelectSegmented.layer.cornerRadius = [self cornerRadius:self.gameModelSelectSegmented.bounds.size.height * 3];  31       32     [self.gameModelSelectSegmented addTarget:self action:@selector(segmentAction:) forControlEvents:UIControlEventValueChanged];//target-action  33       34     _selfDefiningModel = 2;//default model  35 }  36   37 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender  38 {  39     if ([segue.identifier isEqualToString:@"show PlayingCardGameHistory"])  40     {  41         if ([segue.destinationViewController isKindOfClass:[HistoryViewController class]])  42         {  43             HistoryViewController *HVC = (HistoryViewController *)segue.destinationViewController;  44             HVC.history = self.game.gameStateHistory;  45         }  46     }  47 }  48   49   50 - (void) segmentAction:(UISegmentedControl *)Seg  51 {  52     if  (self.gameModelSelectSegmented.selectedSegmentIndex == 2)  53     {  54         [self assertSelfDefiningModel:self.matchModelTextFiled.text];  55     }  56     else  57     {  58         self.selfDefiningModel = self.gameModelSelectSegmented.selectedSegmentIndex + 2;  59     }  60     self.gameModelLable.text = [NSString stringWithFormat:@"game model:%lu",(unsigned long)self.selfDefiningModel];  61 }  62   63 - (void) assertSelfDefiningModel:(NSString *)text  64 {  65     if ([self.matchModelTextFiled.text integerValue] < 2)  66     {  67         [[[UIAlertView alloc] initWithTitle:@"Wrong" message:@"game model at least 2" delegate:nil cancelButtonTitle:@"certain" otherButtonTitles:nil, nil] show];  68         self.matchModelTextFiled.text = @"";  69         self.gameModelSelectSegmented.selectedSegmentIndex = 0;  70     }  71     else if ([self.matchModelTextFiled.text integerValue] > [self cardButtonsNumber])  72     {  73         [[[UIAlertView alloc] initWithTitle:@"Wrong" message:@"beyond card max number" delegate:nil cancelButtonTitle:@"certain" otherButtonTitles:nil, nil] show];  74         self.matchModelTextFiled.text = @"";  75         self.gameModelSelectSegmented.selectedSegmentIndex = 0;  76     }  77     else  78     {  79         self.selfDefiningModel = [self.matchModelTextFiled.text integerValue];  80     }  81 }  82   83 - (Deck *)createDeck  84 {  85     return [[PlayingCardDeck alloc] init];  86 }  87   88 - (IBAction)touchRestartButton  89 {  90     //恢复默认值  91     self.gameModelSelectSegmented.enabled = YES;  92     self.matchModelTextFiled.enabled = YES;  93     self.matchModelTextFiled.enabled = YES;  94     self.gameModelSelectSegmented.selectedSegmentIndex = 0;  95     self.selfDefiningModel = 2;  96     self.gameModelLable.text = [NSString stringWithFormat:@"game model:%d",_selfDefiningModel];  97     self.matchModelTextFiled.text = nil;  98       99     self.game = nil; 100     [self updateUIWithNotCreateGame]; 101 } 102  103 - (IBAction)touchCardButton:(UIButton *)sender 104 { 105     //游戏开始后禁用模式选择功能 106     self.gameModelSelectSegmented.enabled = NO; 107     self.matchModelTextFiled.enabled = NO; 108     self.matchModelTextFiled.enabled = NO; 109      110     NSUInteger cardIndex = [self.cardButtons indexOfObject:sender]; 111     if(!self.game) 112     { 113         self.game = [[PlayingCardMatchingGame alloc] initWithCardCount:[self.cardButtons count] 114                                                              usingDeck:[self createDeck]]; 115         self.game.gameModel = self.selfDefiningModel; 116     } 117     [self.game chooseCardAtIndex:cardIndex]; 118     [self updateUI]; 119 } 120  121 - (void) updateUIWithNotCreateGame 122 { 123     for (UIButton *cardButton in self.cardButtons) 124     { 125         [cardButton setTitle:@"" forState:UIControlStateNormal]; 126         [cardButton setBackgroundImage:[UIImage imageNamed:@"cardBack"] forState:UIControlStateNormal]; 127         cardButton.enabled = YES; 128     } 129     self.gameStateLable.text = @"State"; 130     self.scoreLable.text = @"Score:0"; 131 } 132  133 - (void)updateUI 134 { 135     [super updateUI]; 136     self.gameStateLable.text = ((PlayingCardMatchingGame *)self.game).gameState; 137 } 138  139 - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 140 { 141     [self.view endEditing:YES]; 142 } 143  144 @end

SetCardGameViewController

本类实现了SetCard匹配游戏,因为是UI层,因此使用了NSAttributeString来表示SetCard的内容,同样也实现了History功能

注意:由于SetCard牌面始终朝上,因此要在ViewDidLoad中初始化game

1 #import "ViewController.h" 2  3 @interface SetCardGameViewController : ViewController 4  5 @end
  1 #import "SetCardGameViewController.h"   2 #import "SetCardMatchingGame.h"   3 #import "SetCardDeck.h"   4 #import "SetCard.h"   5 #import "HistoryViewController.h"   6    7 @interface SetCardGameViewController()   8    9 @property (weak, nonatomic) IBOutlet UILabel *gameStateLable;  10 @property (weak, nonatomic) IBOutlet UIButton *restartButton;  11 @property (nonatomic) NSUInteger selfDefiningModel;  12 @property (nonatomic,strong) NSDictionary *colorOfCard;  13   14 @end  15   16 @implementation SetCardGameViewController  17   18 #define CORNER_FONT_STANDARD_HIGHT 180.0  19 #define CORNER_RADIUS 12.0  20   21 //让界面变得更好看的魔法  22 - (CGFloat)cornerScaleFactor:(CGFloat)height {return height / CORNER_FONT_STANDARD_HIGHT;}  23 - (CGFloat)cornerRadius:(CGFloat)height {return CORNER_RADIUS * [self cornerScaleFactor:height];}  24   25 - (void)viewDidLoad  26 {  27     //又是个让界面变得更好看的魔法  28     self.restartButton.layer.cornerRadius = [self cornerRadius:self.restartButton.bounds.size.height];  29       30     self.colorOfCard = @{@"red" : [UIColor redColor], @"green" : [UIColor greenColor], @"purple" : [UIColor purpleColor]};  31       32     _selfDefiningModel = 3;//default model  33       34     if(!self.game)  35     {  36         self.game = [[SetCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]  37                                                          usingDeck:[self createDeck]];  38         self.game.gameModel = self.selfDefiningModel;  39     }  40     [self updateUIWithNotCreateGame];  41 }  42   43 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender  44 {  45     if ([segue.identifier isEqualToString:@"show SetCardGameHistory"])  46     {  47         if ([segue.destinationViewController isKindOfClass:[HistoryViewController class]])  48         {  49             HistoryViewController *HVC = (HistoryViewController *)segue.destinationViewController;  50             HVC.history = self.game.gameStateHistory;  51         }  52     }  53 }  54   55   56 - (Deck *)createDeck  57 {  58     return [[SetCardDeck alloc] init];  59 }  60 - (IBAction)touchCardButtons:(UIButton *)sender  61 {  62     //游戏开始后禁用模式选择功能  63     NSUInteger cardIndex = [self.cardButtons indexOfObject:sender];  64     if(!self.game)  65     {  66         self.game = [[SetCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]  67                                                              usingDeck:[self createDeck]];  68         self.game.gameModel = self.selfDefiningModel;  69     }  70     [self.game chooseCardAtIndex:cardIndex];  71     [self updateUI];  72 }  73   74 - (IBAction)touchRestartButton:(UIButton *)sender  75 {  76     //恢复默认值  77     self.selfDefiningModel = 3;  78     self.game = nil;  79     if(!self.game)  80     {  81         self.game = [[SetCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]  82                                                          usingDeck:[self createDeck]];  83         self.game.gameModel = self.selfDefiningModel;  84     }  85     [self updateUIWithNotCreateGame];  86 }  87   88 - (void) updateUIWithNotCreateGame  89 {  90     for (UIButton *cardButton in self.cardButtons)  91     {  92         NSUInteger cardIndex = [self.cardButtons indexOfObject:cardButton];  93         Card *card = [self.game cardAtIndex:cardIndex];  94         [cardButton setAttributedTitle:[self attributeTitleForCard:card] forState:UIControlStateNormal];  95         [cardButton setBackgroundImage:[UIImage imageNamed:@"cardFront"] forState:UIControlStateNormal];  96         cardButton.enabled = YES;  97     }  98     self.gameStateLable.text = @"State";  99     self.scoreLable.text = @"Score:0"; 100 } 101  102 - (void)updateUI 103 { 104     for (UIButton *cardButton in self.cardButtons) 105     { 106         NSUInteger cardIndex = [self.cardButtons indexOfObject:cardButton]; 107         Card *card = [self.game cardAtIndex:cardIndex]; 108         [cardButton setAttributedTitle:[self attributeTitleForCard:card] forState:UIControlStateNormal]; 109         [cardButton setBackgroundImage:[self backgroundImageForCard:card] forState:UIControlStateNormal]; 110         cardButton.enabled = !card.isMacthed; 111     } 112     self.scoreLable.text = [NSString stringWithFormat:@"Score:%ld",(long)self.game.score]; 113     self.gameStateLable.text = ((SetCardMatchingGame *)self.game).gameState; 114 } 115  116 - (NSAttributedString *)attributeTitleForCard:(Card *)card 117 { 118     NSShadow *shadow1 = [[NSShadow alloc] init]; 119     shadow1.shadowColor = [UIColor grayColor]; 120     shadow1.shadowOffset = CGSizeMake(2.0f, 2.0f); 121      122     NSShadow *shadow2 = [[NSShadow alloc] init]; 123     shadow2.shadowColor = [UIColor whiteColor]; 124     shadow2.shadowOffset = CGSizeMake(0.0F,0.0f); 125     return [[NSAttributedString alloc] initWithString:((SetCard *)card).suit 126                                            attributes:@{NSForegroundColorAttributeName : [self.colorOfCard valueForKey:((SetCard *)card).color], 127                                                                  NSShadowAttributeName : ((SetCard *)card).shading ? shadow1 : shadow2, 128                                                                    NSFontAttributeName : [UIFont boldSystemFontOfSize:10.0f]}]; 129 } 130  131 - (UIImage *)backgroundImageForCard:(Card *)card 132 { 133     return card.chosen ? [UIImage imageNamed:@"setCardback"] : [UIImage imageNamed:@"cardFront"]; 134 } 135  136 @end

d.history功能的实现

HistoryViewController

属性history用于接收父MVC传来的数据

1 #import <UIKit/UIKit.h> 2  3 @interface HistoryViewController : UIViewController 4 @property (strong, nonatomic) NSArray *history; 5 @end
 1 #import "HistoryViewController.h"  2   3 @interface HistoryViewController ()  4 @property (weak, nonatomic) IBOutlet UITextView *HistoryTextView;  5 @end  6   7 @implementation HistoryViewController  8   9 - (void)viewDidLoad { 10     [super viewDidLoad]; 11     // Do any additional setup after loading the view.     12     for (NSString *state in self.history) 13     { 14         self.HistoryTextView.text = [self.HistoryTextView.text stringByAppendingFormat:@"%@/n",state]; 15     } 16 } 17 @end

总结:本次作业提出的问题比解决的问题要多很多,作者只是极其简单的实现了作业的大致要求,仍然有许多问题没有解决,如Model的抽象化问题中原有的readonly属性在超类中可能变为了readwrite,是否会产生安全性问题,有没有更好的方法;Controller的抽象化中将许多原本隐藏在.m文件中的输出口放至超类的.h文件中,是否会造成子类对输出口的滥用,有没有更好的解决方案;仍然没有解决State显示内容超出屏幕空间的问题(太懒了=_=);Setcard的state显示可以使用NSAttributeString(感兴趣可以试试);同样SetCard中history的显示也可以使用NSAttributeString,此时可能要改变一些传值的内容(感兴趣去玩耍一下吧);关于SetCard的游戏规则其实还不太完善,比如初始化后可能无适合匹配情况的判断(百度百科的游戏规则中是要再次添加三张牌,我在屏幕中尽可能多添加了几张纸牌来避免这种情况),以及游戏结束情况的判断,本次作业相当于只实现了setcard选择后的匹配计分规则(任重而道远。。。)。到此就是我大概能想到的值得改进的地方(感觉到提出的问题比解决的问题多了吧=_=),一次作业的内容不仅能复习到前面所学的内容,还是对个人学到知识融汇贯通的一次检验,各位加油,有任何问题欢迎讨论:)

作业源码地址: https://github.com/NSLogMeng/Stanford_iOS7_Study/commit/0bb1103ccb4293caa9dc3b12200ba9fb12a51170

课程视频地址:网易公开课: http://open.163.com/movie/2014/1/L/H/M9H7S9F1H_M9H801GLH.html

或者iTunes U搜索standford课程

正文到此结束
Loading...