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

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

终于有时间跟新了,两周时间复(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//需要跳转时的Action2-(IBAction)rentEquipment3{4if(self.snowTraversingTalent ==Skiing) {5[self performSegueWithIdentifier:@“AskAboutSkis” sender:self];6}else{7[self performSegueWithIdentifier:@“AskAboutSnowboard” sender:self];8}9}1011//为各segue做跳转前的准备12- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {13if([segue.identifier isEqualToString:@“DoSomething”]) {14if([segue.destinationViewController isKindOfClass:[DoSomethingVCclass]]) {15DoSomethingVC *doVC = (DoSomethingVC *)segue.destinationViewController;16doVC.neededInfo =...; }17}18}1920//segue跳转判断21- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {22if([segue.identifier isEqualToString:@“DoAParticularThing”]) {23return[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"23@interfaceSetCard : Card45@property (strong,nonatomic) NSString *suit;//▲ ● ■6@property (strong,nonatomic) NSString *color;//red green purple7@property (nonatomic) BOOL shading;89+ (NSArray *)validSuits;10+ (NSArray *)validColors;11+(NSUInteger)shadingNumber;1213@end

1#import"SetCard.h"23@interfaceSetCard()45@end67@implementationSetCard89- (NSString *)contents10{11returnnil;12}1314- (int)match:(NSArray *)otherCards15{16intscore =0;1718if([otherCards count] ==2)19{20SetCard *firstCard =[otherCards firstObject];21SetCard *secondCard =[otherCards lastObject];22if([self.suit isEqualToString:firstCard.suit] &&[firstCard.suit isEqualToString:secondCard.suit])23{24score +=1;25}26elseif(![self.suit isEqualToString:firstCard.suit] && ![self.suit isEqualToString:secondCard.suit] && ![firstCard.suit isEqualToString:secondCard.suit])27{28score +=1;29}30else31{32score -=1;33}3435if([self.color isEqualToString:firstCard.color] && [firstCard.color isEqualToString:secondCard.color])//全相等36{37score +=1;38}39elseif(![self.color isEqualToString:firstCard.color] && ![self.color isEqualToString:secondCard.color] && ![firstCard.color isEqualToString:secondCard.color])//全不相等40{41score +=1;42}43else44{45score -=1;46}4748if((self.shading == firstCard.shading) && (firstCard.shading ==secondCard.shading))49{50score +=1;51}52else53{54score -=1;55}5657}5859if(score ==3)60{61score =1;62}63else64{65score =0;66}6768returnscore;69}7071+ (NSArray *)validSuits72{73return@[@"▲",@"▲▲",@"▲▲▲",@"●",@"●●",@"●●●",@"■",@"■ ■",@"■ ■ ■"];74}7576+ (NSArray *)validColors77{78return@[@"red",@"green",@"purple"];79}808182+(NSUInteger)shadingNumber83{84return2;85}

SetCardDeck

与PlayingCardDeck类似

1#import"Deck.h"23@interfaceSetCardDeck : Deck45@end

1#import"SetCardDeck.h"2#import"SetCard.h"34@implementationSetCardDeck56-(instancetype) init7{8self =[super init];9if(self)10{11for(NSString *suitin[SetCard validSuits])12{13for(NSString *colorin[SetCard validColors])14{15for(NSUInteger i =0; i < [SetCard shadingNumber]; i++)16{17SetCard *card =[[SetCard alloc] init];18card.suit =suit;19card.color =color;20card.shading = (i<1) ?true:false;2122[self addCard:card];23}24}25}26}27returnself;28}2930@end

CardMatchingGame的抽象化

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

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

1#import<Foundation/Foundation.h>2#import"Deck.h"3#import"Card.h"45staticconstintMISMATCH_PENALTY =2;6staticconstintMATCH_BOUNDS =4;7staticconstintCOST_TO_CHOOSE =1;89@interfaceCardMatchingGame : NSObject1011//designated initializer12-(instancetype) initWithCardCount:(NSUInteger)count13usingDeck:(Deck *)deck;14- (void) chooseCardAtIndex:(NSUInteger)index;15- (Card *) cardAtIndex:(NSUInteger)index;1617- (NSString *)validOfOtherCards:(NSArray *)otherCards;1819@property (nonatomic,readonly) NSString *validOfOtherCards;20@property (nonatomic,readonly) NSInteger score;21@property (nonatomic) NSUInteger gameModel;//>=222@property (nonatomic,strong) NSMutableArray *gameStateHistory;2324@end

1#import"CardMatchingGame.h"23@interfaceCardMatchingGame()45@property (nonatomic,readwrite) NSString *validOfOtherCards;6@property (nonatomic,readwrite) NSInteger score;7@property (nonatomic,strong) NSMutableArray *cards;//of playingcard89@end1011@implementationCardMatchingGame1213- (NSMutableArray *)cards14{15if(!_cards) _cards =[[NSMutableArray alloc] init];16return_cards;17}1819- (NSMutableArray *)gameStateHistory20{21if(!_gameStateHistory)22{23_gameStateHistory =[[NSMutableArray alloc] init];24}25return_gameStateHistory;26}2728- (instancetype) initWithCardCount:(NSUInteger)count usingDeck:(Deck *)deck29{30self =[super init];31if(self)32{33for(inti =0; i < count; i++)34{35Card *card =[deck drawRandomCard];36if(card)37{38[self.cards addObject:card];39}40else41{42self =nil;43

break;44}45}46}4748returnself;49}5051- (Card *) cardAtIndex:(NSUInteger)index52{53return(index < [self.cards count]) ?self.cards[index] : nil;54}5556- (void) chooseCardAtIndex:(NSUInteger)index57{58Card *card =[self cardAtIndex:index];59if(!card.isMacthed)60{61if(card.isChosen)62{63card.chosen =NO;64}65else66{67//match against other chosen cards68NSMutableArray *otherCards =[NSMutableArray arrayWithCapacity:self.gameModel];6970for(Card *otherCardinself.cards)71{72if(otherCard.isChosen && !otherCard.isMacthed)73{74[otherCards addObject:otherCard];75}76}7778//不能放于for循环之前,否则会将本次被选择的牌加入cards,不能放于下面的if之后,否则当if成立时返回,没有将本次翻牌的cost记录,且不能翻牌79self.score -= COST_TO_CHOOSE *self.gameModel;80card.chosen =YES;8182if([otherCards count] < self.gameModel -1)83{84return;85}86else87{88self.validOfOtherCards =[self validOfOtherCards:otherCards];89intmatchScore =[card match:otherCards];9091if(matchScore)92{93self.score += matchScore * MATCH_BOUNDS *self.gameModel;94for(Card *otherCardinotherCards)95{96otherCard.matched =YES;97}98card.matched =YES;99}100else101{102self.score -= MISMATCH_PENALTY *self.gameModel;103for(Card *otherCardinotherCards)104{105otherCard.chosen =NO;106}107}108}109}110}111}112113- (NSString *)validOfOtherCards:(NSArray *)otherCards114{115NSMutableString *string=[[NSMutableString alloc] init];116for(Card *cardinotherCards)117{118[stringappendFormat:@"%@",card.contents];119}120returnstring;121}122123@end

子类化的PlayingCardMatchingGame

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

1#import"CardMatchingGame.h"23@interfacePlayingCardMatchingGame : CardMatchingGame45@property (nonatomic,readonly) NSString *gameState;67@end

1#import"PlayingCardMatchingGame.h"23@interfacePlayingCardMatchingGame()45@property (nonatomic,readwrite) NSString *gameState;67@end89@implementationPlayingCardMatchingGame1011- (void)chooseCardAtIndex:(NSUInteger)index12{13intforwardScore =self.score;14[super chooseCardAtIndex:index];15[self updateGameState:forwardScore withIndexOfCard:index];16}1718- (void)updateGameState:(int)forwardScore withIndexOfCard:(int)index19{20if(self.score == forwardScore - self.gameModel *COST_TO_CHOOSE)21{22_gameState =[self cardAtIndex:index].contents;23}24elseif(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}29elseif(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}3536@end

子类化的SetCardMatchingGame

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

1#import"CardMatchingGame.h"23@interfaceSetCardMatchingGame : CardMatchingGame45@property (nonatomic,readonly) NSString *gameState;67@end

1#import"SetCardMatchingGame.h"2#import"SetCard.h"34@interfaceSetCardMatchingGame()56@property (nonatomic,readwrite) NSString *gameState;78@end910@implementationSetCardMatchingGame1112- (void)chooseCardAtIndex:(NSUInteger)index13{14intforwardScore =self.score;15[super chooseCardAtIndex:index];16[self updateGameState:forwardScore withIndexOfCard:index];17}1819- (NSString *)validOfOtherCards:(NSArray *)otherCards20{21NSMutableString *string=[[NSMutableString alloc] init];22for(SetCard *cardinotherCards)23{24[stringappendString:card.suit];25}26returnstring;27}2829- (void)updateGameState:(int)forwardScore withIndexOfCard:(int)index30{31if(self.score == forwardScore - (self.gameModel *COST_TO_CHOOSE))32{33_gameState = ((SetCard *)[self cardAtIndex:index]).suit;34}35elseif(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}40elseif(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}4647@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"34@classCardMatchingGame;56@interfaceViewController : UIViewController78//protected9//for subclasses10- (Deck *)createDeck;//abstract1112@property (strong,nonatomic) CardMatchingGame *game;13@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;14@property (weak, nonatomic) IBOutlet UILabel *scoreLable;1516-(NSUInteger)cardButtonsNumber;17- (void) updateUI;18- (NSString *)titleForCard:(Card *)card;19- (UIImage *)backgroundImageForCard:(Card *)card;2021@end

1#import"ViewController.h"2#import"CardMatchingGame.h"34@interfaceViewController ()56@end78@implementationViewController910-(NSUInteger)cardButtonsNumber11{12return[_cardButtons count];13}1415- (Deck *)createDeck//abstract16{17returnnil;18}1920- (IBAction)touchCardButton:(UIButton *)sender21{22NSUInteger cardIndex =[self.cardButtons indexOfObject:sender];23[self.game chooseCardAtIndex:cardIndex];24[self updateUI];25}2627- (void) updateUI28{29for(UIButton *cardButtoninself.cardButtons)30{31NSUInteger cardIndex =[self.cardButtons indexOfObject:cardButton];32Card *card =[self.game cardAtIndex:cardIndex];33[cardButton setTitle:[self titleForCard:card] forState:UIControlStateNormal];34[cardButton setBackgroundImage:[self backgroundImageForCard:card] forState:UIControlStateNormal];35cardButton.enabled = !card.isMacthed;36}37self.scoreLable.text = [NSString stringWithFormat:@"Score:%ld",(long)self.game.score];38}3940- (NSString *)titleForCard:(Card *)card41{42returncard.isChosen ?card.contents : nil;43}4445- (UIImage *)backgroundImageForCard:(Card *)card46{47return[UIImage imageNamed:card.isChosen ?@"cardFront":@"cardBack"];48}4950@end

PlayingCardGameController

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

1#import"ViewController.h"23@interfacePlayingCardGameViewController : ViewController45@end

1#import"PlayingCardGameViewController.h"2#import"PlayingCardDeck.h"3#import"PlayingCardMatchingGame.h"4#import"HistoryViewController.h"56@interfacePlayingCardGameViewController ()78@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;1415@end1617@implementationPlayingCardGameViewController1819#defineCORNER_FONT_STANDARD_HIGHT 180.020#defineCORNER_RADIUS 12.02122//让界面变得更好看的魔法23- (CGFloat)cornerScaleFactor:(CGFloat)height {returnheight /CORNER_FONT_STANDARD_HIGHT;}24- (CGFloat)cornerRadius:(CGFloat)height {returnCORNER_RADIUS *[self cornerScaleFactor:height];}2526- (void)viewDidLoad27{28//又是个让界面变得更好看的魔法29self.restartButton.layer.cornerRadius =[self cornerRadius:self.restartButton.bounds.size.height];30self.gameModelSelectSegmented.layer.cornerRadius = [self cornerRadius:self.gameModelSelectSegmented.bounds.size.height *3];3132[self.gameModelSelectSegmented addTarget:self action:@selector(segmentAction:) forControlEvents:UIControlEventValueChanged];//target-action3334_selfDefiningModel =2;//default model35}3637- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender38{39if([segue.identifier isEqualToString:@"show PlayingCardGameHistory"])40{41if([segue.destinationViewController isKindOfClass:[HistoryViewControllerclass]])42{43HistoryViewController *HVC = (HistoryViewController *)segue.destinationViewController;44HVC.history =self.game.gameStateHistory;45}46}47}484950- (void) segmentAction:(UISegmentedControl *)Seg51{52if(self.gameModelSelectSegmented.selectedSegmentIndex ==2)53{54[self assertSelfDefiningModel:self.matchModelTextFiled.text];55}56else57{58self.selfDefiningModel = self.gameModelSelectSegmented.selectedSegmentIndex +2;59}60self.gameModelLable.text = [NSString stringWithFormat:@"game model:%lu",(unsignedlong)self.selfDefiningModel];61}6263- (void) assertSelfDefiningModel:(NSString *)text64{65if([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];68self.matchModelTextFiled.text =@"";69self.gameModelSelectSegmented.selectedSegmentIndex =0;70}71elseif([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];74self.matchModelTextFiled.text =@"";75self.gameModelSelectSegmented.selectedSegmentIndex =0;76}77else78{79self.selfDefiningModel =[self.matchModelTextFiled.text integerValue];80}81}8283- (Deck *)createDeck84{85return[[PlayingCardDeck alloc] init];86}8788-(IBAction)touchRestartButton89{90//恢复默认值91self.gameModelSelectSegmented.enabled =YES;92self.matchModelTextFiled.enabled =YES;93self.matchModelTextFiled.enabled =YES;94self.gameModelSelectSegmented.selectedSegmentIndex =0;95self.selfDefiningModel =2;96self.gameModelLable.text = [NSString stringWithFormat:@"game model:%d",_selfDefiningModel];97self.matchModelTextFiled.text =nil;9899self.game =nil;100[self updateUIWithNotCreateGame];101}102103- (IBAction)touchCardButton:(UIButton *)sender104{105//游戏开始后禁用模式选择功能106self.gameModelSelectSegmented.enabled =NO;107self.matchModelTextFiled.enabled =NO;108self.matchModelTextFiled.enabled =NO;109110NSUInteger cardIndex =[self.cardButtons indexOfObject:sender];111if(!self.game)112{113self.game =[[PlayingCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]114usingDeck:[self createDeck]];115self.game.gameModel =self.selfDefiningModel;116}117[self.game chooseCardAtIndex:cardIndex];118[self updateUI];119}120121- (void) updateUIWithNotCreateGame122{123for(UIButton *cardButtoninself.cardButtons)124{125[cardButton setTitle:@""forState:UIControlStateNormal];126[cardButton setBackgroundImage:[UIImage imageNamed:@"cardBack"] forState:UIControlStateNormal];127cardButton.enabled =YES;128}129self.gameStateLable.text =@"State";130self.scoreLable.text =@"Score:0";131}132133- (void)updateUI134{135[super updateUI];136self.gameStateLable.text = ((PlayingCardMatchingGame *)self.game).gameState;137}138139- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event140{141[self.view endEditing:YES];142}143144@end

SetCardGameViewController

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

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

1#import"ViewController.h"23@interfaceSetCardGameViewController : ViewController45@end

1#import"SetCardGameViewController.h"2#import"SetCardMatchingGame.h"3#import"SetCardDeck.h"4#import"SetCard.h"5#import"HistoryViewController.h"67@interfaceSetCardGameViewController()89@property (weak, nonatomic) IBOutlet UILabel *gameStateLable;10@property (weak, nonatomic) IBOutlet UIButton *restartButton;11@property (nonatomic) NSUInteger selfDefiningModel;12@property (nonatomic,strong) NSDictionary *colorOfCard;1314@end1516@implementationSetCardGameViewController1718#defineCORNER_FONT_STANDARD_HIGHT 180.019#defineCORNER_RADIUS 12.02021//让界面变得更好看的魔法22- (CGFloat)cornerScaleFactor:(CGFloat)height {returnheight /CORNER_FONT_STANDARD_HIGHT;}23- (CGFloat)cornerRadius:(CGFloat)height {returnCORNER_RADIUS *[self cornerScaleFactor:height];}2425- (void)viewDidLoad26{27//又是个让界面变得更好看的魔法28self.restartButton.layer.cornerRadius =[self cornerRadius:self.restartButton.bounds.size.height];2930self.colorOfCard = @{@"red": [UIColor redColor],@"green": [UIColor greenColor],@"purple": [UIColor purpleColor]};3132_selfDefiningModel =3;//default model3334if(!self.game)35{36self.game =[[SetCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]37usingDeck:[self createDeck]];38self.game.gameModel =self.selfDefiningModel;39}40[self updateUIWithNotCreateGame];41}4243- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender44{45if([segue.identifier isEqualToString:@"show SetCardGameHistory"])46{47if([segue.destinationViewController isKindOfClass:[HistoryViewControllerclass]])48{49HistoryViewController *HVC = (HistoryViewController *)segue.destinationViewController;50HVC.history =self.game.gameStateHistory;51}52}53}545556- (Deck *)createDeck57{58return[[SetCardDeck alloc] init];59}60- (IBAction)touchCardButtons:(UIButton *)sender61{62//游戏开始后禁用模式选择功能63NSUInteger cardIndex =[self.cardButtons indexOfObject:sender];64if(!self.game)65{66self.game =[[SetCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]67usingDeck:[self createDeck]];68self.game.gameModel =self.selfDefiningModel;69}70[self.game chooseCardAtIndex:cardIndex];71[self updateUI];72}7374- (IBAction)touchRestartButton:(UIButton *)sender75{76//恢复默认值77self.selfDefiningModel =3;78self.game =nil;79if(!self.game)80{81self.game =[[SetCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]82usingDeck:[self createDeck]];83self.game.gameModel =self.selfDefiningModel;84}85[self updateUIWithNotCreateGame];86}8788- (void) updateUIWithNotCreateGame89{90for(UIButton *cardButtoninself.cardButtons)91{92NSUInteger cardIndex =[self.cardButtons indexOfObject:cardButton];93Card *card =[self.game cardAtIndex:cardIndex];94[cardButton setAttributedTitle:[self attributeTitleForCard:card] forState:UIControlStateNormal];95[cardButton setBackgroundImage:[UIImage imageNamed:@"cardFront"] forState:UIControlStateNormal];96cardButton.enabled =YES;97}98self.gameStateLable.text =@"State";99self.scoreLable.text =@"Score:0";100}101102- (void)updateUI103{104for(UIButton *cardButtoninself.cardButtons)105{106NSUInteger cardIndex =[self.cardButtons indexOfObject:cardButton];107Card *card =[self.game cardAtIndex:cardIndex];108[cardButton setAttributedTitle:[self attributeTitleForCard:card] forState:UIControlStateNormal];109[cardButton setBackgroundImage:[self backgroundImageForCard:card] forState:UIControlStateNormal];110cardButton.enabled = !card.isMacthed;111}112self.scoreLable.text = [NSString stringWithFormat:@"Score:%ld",(long)self.game.score];113self.gameStateLable.text = ((SetCardMatchingGame *)self.game).gameState;114}115116- (NSAttributedString *)attributeTitleForCard:(Card *)card117{118NSShadow *shadow1 =[[NSShadow alloc] init];119shadow1.shadowColor =[UIColor grayColor];120shadow1.shadowOffset = CGSizeMake(2.0f,2.0f);121122NSShadow *shadow2 =[[NSShadow alloc] init];123shadow2.shadowColor =[UIColor whiteColor];124shadow2.shadowOffset = CGSizeMake(0.0F,0.0f);125return[[NSAttributedString alloc] initWithString:((SetCard *)card).suit126attributes:@{NSForegroundColorAttributeName : [self.colorOfCard valueForKey:((SetCard *)card).color],127NSShadowAttributeName : ((SetCard *)card).shading ?shadow1 : shadow2,128NSFontAttributeName : [UIFont boldSystemFontOfSize:10.0f]}];129}130131- (UIImage *)backgroundImageForCard:(Card *)card132{133returncard.chosen ? [UIImage imageNamed:@"setCardback"] : [UIImage imageNamed:@"cardFront"];134}135136@end

d.history功能的实现

HistoryViewController

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

1#import<UIKit/UIKit.h>23@interfaceHistoryViewController : UIViewController4@property (strong, nonatomic) NSArray *history;5@end

1#import"HistoryViewController.h"23@interfaceHistoryViewController ()4@property (weak, nonatomic) IBOutlet UITextView *HistoryTextView;5@end67@implementationHistoryViewController89- (void)viewDidLoad {10[super viewDidLoad];11//Do any additional setup after loading the view.12for(NSString *stateinself.history)13{14self.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课程

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航