(6/18)重學Standford_iOS7開發_控制器多態性、導航控制器、選項卡欄控制器_課程筆記

  終於有時間跟新了,兩週時間復(yu)習(xi)了5門考試累覺不愛。。。。。。html

--------------------------------------------------------------------------我是正文分割線---------------------------------------------------------------------------------------------git

第六課github

  一、控制器多態性數組

    這裏控制器多態性是指在控制器中使用繼承,經過繼承構造通用視圖控制器(ViewController),在具體的MVC中繼承通用視圖控制器實現具體功能(一般是父類中的抽象方法)安全

    注意:objective-C並無abstract關鍵字,所以一般要經過註釋的方式說明,抽象方法須要在.h文件中聲明,通常實現文件中的抽象方法沒有具體功能,只返回nil。app

    storyboard的多態控制器類須要在屬性檢查器中更改指定的類。dom

    說明:經過繼承,父類中的鏈接也會被完整的繼承下來,如輸出口(Outlet)等。ide

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

  二、多MVCatom

    理解:多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;

  三、做業

    要求: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:@"%@\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課程

相關文章
相關標籤/搜索