• (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)呈现。

          

        (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 或 百度百科

      作业解析:

          最终实现效果如下图:

            

          实现过程:

             针对上次作业的结果对纸牌游戏再次进行抽象化并添加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的更新

      如图:

      由于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:@"%@
    ",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课程

  • 相关阅读:
    最小生成树
    BZOJ3894:文理分科(最大流)(同BZoj3438)
    BZOJ3438:小M的作物 (最大闭合权图->最小割)
    BZOJ 1305:dance跳舞(二分+最大流)
    BZOJ1266:上学路线route (最短路+最小割)
    BZOJ1854:游戏(二分图匹配)
    【PowerOJ1738】最小路径覆盖
    【SPOJ839】Optimal Marks 网络流
    【USACO】AC自动机
    【国家集训队2011】聪聪可可 树分治
  • 原文地址:https://www.cnblogs.com/nslogmeng/p/4557590.html
Copyright © 2020-2023  润新知