(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,方便在代码中使用
//需要跳转时的Action
- (IBAction)rentEquipment
{
if (self.snowTraversingTalent == Skiing) {
[self performSegueWithIdentifier:@“AskAboutSkis” sender:self];
} else {
[self performSegueWithIdentifier:@“AskAboutSnowboard” sender:self];
}
} //为各segue做跳转前的准备
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@“DoSomething”]) {
if ([segue.destinationViewController isKindOfClass:[DoSomethingVC class]]) {
DoSomethingVC *doVC = (DoSomethingVC *)segue.destinationViewController;
doVC.neededInfo = ...; }
}
} //segue跳转判断
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
 if ([segue.identifier isEqualToString:@“DoAParticularThing”]) {
return [self canDoAParticularThing] ? YES : NO;
}
}
注意: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纸牌游戏
抽象化结果:https://github.com/NSLogMeng/Stanford_iOS7_Study/commit/58afa7577cb339328819fccb5422aef27fd843ff
a.添加Set及Model的抽象化
SetCard
SetCard的匹配详见作业要求中的规则,此处判定匹配成功则score为1,否则为0(仅作为判定标记,也可以根据自己喜好自定义分数规则)
#import "Card.h" @interface SetCard : Card @property (strong,nonatomic) NSString *suit;// ▲ ● ■
@property (strong,nonatomic) NSString *color;// red green purple
@property (nonatomic) BOOL shading; + (NSArray *)validSuits;
+ (NSArray *)validColors;
+ (NSUInteger)shadingNumber; @end
#import "SetCard.h" @interface SetCard() @end @implementation SetCard - (NSString *)contents
{
return nil;
} - (int)match:(NSArray *)otherCards
{
int score = ; if ([otherCards count] == )
{
SetCard *firstCard = [otherCards firstObject];
SetCard *secondCard = [otherCards lastObject];
if ([self.suit isEqualToString:firstCard.suit] && [firstCard.suit isEqualToString:secondCard.suit])
{
score += ;
}
else if (![self.suit isEqualToString:firstCard.suit] && ![self.suit isEqualToString:secondCard.suit] && ![firstCard.suit isEqualToString:secondCard.suit])
{
score += ;
}
else
{
score -= ;
} if ([self.color isEqualToString:firstCard.color] && [firstCard.color isEqualToString:secondCard.color])//全相等
{
score += ;
}
else if (![self.color isEqualToString:firstCard.color] && ![self.color isEqualToString:secondCard.color] && ![firstCard.color isEqualToString:secondCard.color])//全不相等
{
score += ;
}
else
{
score -= ;
} if ((self.shading == firstCard.shading) && (firstCard.shading == secondCard.shading))
{
score += ;
}
else
{
score -= ;
} } if (score == )
{
score = ;
}
else
{
score = ;
} return score;
} + (NSArray *)validSuits
{
return @[@"▲",@"▲▲",@"▲▲▲",@"●",@"●●",@"●●●",@"■",@"■ ■",@"■ ■ ■"];
} + (NSArray *)validColors
{
return @[@"red",@"green",@"purple"];
} + (NSUInteger)shadingNumber
{
return ;
}
SetCardDeck
与PlayingCardDeck类似
#import "Deck.h" @interface SetCardDeck : Deck @end
#import "SetCardDeck.h"
#import "SetCard.h" @implementation SetCardDeck - (instancetype) init
{
self = [super init];
if (self)
{
for (NSString *suit in [SetCard validSuits])
{
for (NSString *color in [SetCard validColors])
{
for (NSUInteger i = ; i < [SetCard shadingNumber]; i++)
{
SetCard *card = [[SetCard alloc] init];
card.suit = suit;
card.color = color;
card.shading = (i<) ? true : false; [self addCard:card];
}
}
}
}
return self;
} @end
CardMatchingGame的抽象化
上次作业中我们的CardMatchingGame只针对PlayingCard的匹配规则及方法在SetCard中任然类似或适用,因此我们抽象化CardMatchingGame,使其成为PlayingCardMatchingGame与SetCardMatchingGame的超类。
首先我们根据作业要求我们添加gameStateHistory属性来保存游戏进行的历史数据,由于可能会出现多纸牌匹配,因此添加validOfOtherCards属性来获取其余纸牌的内容。因为gameState属性最好使用只读属性,因此我们在超类中去除此属性而在子类中实现
#import <Foundation/Foundation.h>
#import "Deck.h"
#import "Card.h" static const int MISMATCH_PENALTY = ;
static const int MATCH_BOUNDS = ;
static const int COST_TO_CHOOSE = ; @interface CardMatchingGame : NSObject // designated initializer
- (instancetype) initWithCardCount:(NSUInteger)count
usingDeck:(Deck *)deck;
- (void) chooseCardAtIndex:(NSUInteger)index;
- (Card *) cardAtIndex:(NSUInteger)index; - (NSString *)validOfOtherCards:(NSArray *)otherCards; @property (nonatomic,readonly) NSString *validOfOtherCards;
@property (nonatomic,readonly) NSInteger score;
@property (nonatomic) NSUInteger gameModel;// >=2
@property (nonatomic,strong) NSMutableArray *gameStateHistory; @end
#import "CardMatchingGame.h" @interface CardMatchingGame() @property (nonatomic,readwrite) NSString *validOfOtherCards;
@property (nonatomic,readwrite) NSInteger score;
@property (nonatomic,strong) NSMutableArray *cards;//of playingcard @end @implementation CardMatchingGame - (NSMutableArray *)cards
{
if (!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
} - (NSMutableArray *)gameStateHistory
{
if (!_gameStateHistory)
{
_gameStateHistory = [[NSMutableArray alloc] init];
}
return _gameStateHistory;
} - (instancetype) initWithCardCount:(NSUInteger)count usingDeck:(Deck *)deck
{
self = [super init];
if (self)
{
for (int i = ; i < count; i++)
{
Card *card = [deck drawRandomCard];
if (card)
{
[self.cards addObject:card];
}
else
{
self = nil;
break;
}
}
} return self;
} - (Card *) cardAtIndex:(NSUInteger)index
{
return (index < [self.cards count]) ? self.cards[index] : nil;
} - (void) chooseCardAtIndex:(NSUInteger)index
{
Card *card = [self cardAtIndex:index];
if (!card.isMacthed)
{
if (card.isChosen)
{
card.chosen = NO;
}
else
{
// match against other chosen cards
NSMutableArray *otherCards = [NSMutableArray arrayWithCapacity:self.gameModel]; for (Card *otherCard in self.cards)
{
if (otherCard.isChosen && !otherCard.isMacthed)
{
[otherCards addObject:otherCard];
}
} //不能放于for循环之前,否则会将本次被选择的牌加入cards,不能放于下面的if之后,否则当if成立时返回,没有将本次翻牌的cost记录,且不能翻牌
self.score -= COST_TO_CHOOSE * self.gameModel;
card.chosen = YES; if ([otherCards count] < self.gameModel - )
{
return;
}
else
{
self.validOfOtherCards = [self validOfOtherCards:otherCards];
int matchScore = [card match:otherCards]; if (matchScore)
{
self.score += matchScore * MATCH_BOUNDS * self.gameModel;
for (Card *otherCard in otherCards)
{
otherCard.matched = YES;
}
card.matched = YES;
}
else
{
self.score -= MISMATCH_PENALTY * self.gameModel;
for (Card *otherCard in otherCards)
{
otherCard.chosen = NO;
}
}
}
}
}
} - (NSString *)validOfOtherCards:(NSArray *)otherCards
{
NSMutableString *string = [[NSMutableString alloc] init];
for (Card *card in otherCards)
{
[string appendFormat:@"%@ ",card.contents];
}
return string;
} @end
子类化的PlayingCardMatchingGame
实现类gameState的记录,并添加方法使其在纸牌匹配时记录状态及状态历史
#import "CardMatchingGame.h" @interface PlayingCardMatchingGame : CardMatchingGame @property (nonatomic,readonly) NSString *gameState; @end
#import "PlayingCardMatchingGame.h" @interface PlayingCardMatchingGame() @property (nonatomic,readwrite) NSString *gameState; @end @implementation PlayingCardMatchingGame - (void)chooseCardAtIndex:(NSUInteger)index
{
int forwardScore = self.score;
[super chooseCardAtIndex:index];
[self updateGameState:forwardScore withIndexOfCard:index];
} - (void)updateGameState:(int)forwardScore withIndexOfCard:(int)index
{
if (self.score == forwardScore - self.gameModel * COST_TO_CHOOSE)
{
_gameState = [self cardAtIndex:index].contents;
}
else if (self.score < (forwardScore - self.gameModel * COST_TO_CHOOSE))
{
_gameState = [NSString stringWithFormat:@"%@ with %@ not matched. %d point penalty!",[self cardAtIndex:index].contents,self.validOfOtherCards,forwardScore - self.score];
[self.gameStateHistory addObject:_gameState];
}
else if (self.score > (forwardScore - self.gameModel * COST_TO_CHOOSE))
{
_gameState = [NSString stringWithFormat:@"%@ matched %@ . get %d score!",[self cardAtIndex:index].contents,self.validOfOtherCards,self.score - forwardScore];
[self.gameStateHistory addObject:_gameState];
}
} @end
子类化的SetCardMatchingGame
与PlayingCardMatchingGame类似,注意在SetCard中并没有content的数据,因此要重写validOfOtherCard:方法,以保证正确获取SetCard的内容
#import "CardMatchingGame.h" @interface SetCardMatchingGame : CardMatchingGame @property (nonatomic,readonly) NSString *gameState; @end
#import "SetCardMatchingGame.h"
#import "SetCard.h" @interface SetCardMatchingGame() @property (nonatomic,readwrite) NSString *gameState; @end @implementation SetCardMatchingGame - (void)chooseCardAtIndex:(NSUInteger)index
{
int forwardScore = self.score;
[super chooseCardAtIndex:index];
[self updateGameState:forwardScore withIndexOfCard:index];
} - (NSString *)validOfOtherCards:(NSArray *)otherCards
{
NSMutableString *string = [[NSMutableString alloc] init];
for (SetCard *card in otherCards)
{
[string appendString:card.suit];
}
return string;
} - (void)updateGameState:(int)forwardScore withIndexOfCard:(int)index
{
if (self.score == forwardScore - (self.gameModel * COST_TO_CHOOSE))
{
_gameState = ((SetCard *)[self cardAtIndex:index]).suit;
}
else if (self.score < (forwardScore - (self.gameModel * COST_TO_CHOOSE)))
{
_gameState = [NSString stringWithFormat:@"%@ with %@ not matched. %d point penalty!",((SetCard *)[self cardAtIndex:index]).suit,self.validOfOtherCards,forwardScore - self.score];
[self.gameStateHistory addObject:_gameState];
}
else if (self.score > (forwardScore - (self.gameModel * COST_TO_CHOOSE)))
{
_gameState = [NSString stringWithFormat:@"%@ matched %@ . get %d score!",((SetCard *)[self cardAtIndex:index]).suit,self.validOfOtherCards,self.score - forwardScore];
[self.gameStateHistory addObject:_gameState];
}
} @end
b.UI的更新
如图:
由于SetCard的游戏规则,SetCard并不会被翻回到背面,因此要更改纸牌的牌面图片
c.Controller的抽象化及子类化实现
上次作业中针对ViewController有很多只与PlayingCardMatchingGame有关的内容,此次将这些内容转移到PlayingCardGameViewController中,对ViewController进行抽象化,并且也在子类SetCardGameViewController中实现SetCard匹配游戏
ViewController
由于许多有关UI的操作需要在子类中实现,因此将他们放到.h文件中,同时titleForCard:与backgroundImageForCard:方法也许要在SetCard子类中重写(想想为什么?),因此也放入.h文件中
#import <UIKit/UIKit.h>
#import "Deck.h" @class CardMatchingGame; @interface ViewController : UIViewController //protected
//for subclasses
- (Deck *)createDeck; // abstract @property (strong,nonatomic) CardMatchingGame *game;
@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
@property (weak, nonatomic) IBOutlet UILabel *scoreLable; - (NSUInteger)cardButtonsNumber;
- (void) updateUI;
- (NSString *)titleForCard:(Card *)card;
- (UIImage *)backgroundImageForCard:(Card *)card; @end
#import "ViewController.h"
#import "CardMatchingGame.h" @interface ViewController () @end @implementation ViewController - (NSUInteger)cardButtonsNumber
{
return [_cardButtons count];
} - (Deck *)createDeck // abstract
{
return nil;
} - (IBAction)touchCardButton:(UIButton *)sender
{
NSUInteger cardIndex = [self.cardButtons indexOfObject:sender];
[self.game chooseCardAtIndex:cardIndex];
[self updateUI];
} - (void) updateUI
{
for (UIButton *cardButton in self.cardButtons)
{
NSUInteger cardIndex = [self.cardButtons indexOfObject:cardButton];
Card *card = [self.game cardAtIndex:cardIndex];
[cardButton setTitle:[self titleForCard:card] forState:UIControlStateNormal];
[cardButton setBackgroundImage:[self backgroundImageForCard:card] forState:UIControlStateNormal];
cardButton.enabled = !card.isMacthed;
}
self.scoreLable.text = [NSString stringWithFormat:@"Score:%ld",(long)self.game.score];
} - (NSString *)titleForCard:(Card *)card
{
return card.isChosen ? card.contents : nil;
} - (UIImage *)backgroundImageForCard:(Card *)card
{
return [UIImage imageNamed:card.isChosen ? @"cardFront" : @"cardBack"];
} @end
PlayingCardGameController
与上次作业基本相同,只不过变为从更抽象的超类中继承为子类(一些只适用于PlayingCardGame的UI与方法在本类实现),并且添加了History功能
#import "ViewController.h" @interface PlayingCardGameViewController : ViewController @end
#import "PlayingCardGameViewController.h"
#import "PlayingCardDeck.h"
#import "PlayingCardMatchingGame.h"
#import "HistoryViewController.h" @interface PlayingCardGameViewController () @property (weak, nonatomic) IBOutlet UISegmentedControl *gameModelSelectSegmented;
@property (weak, nonatomic) IBOutlet UILabel *gameModelLable;
@property (weak, nonatomic) IBOutlet UITextField *matchModelTextFiled;
@property (weak, nonatomic) IBOutlet UILabel *gameStateLable;
@property (weak, nonatomic) IBOutlet UIButton *restartButton;
@property (nonatomic) NSUInteger selfDefiningModel; @end @implementation PlayingCardGameViewController #define CORNER_FONT_STANDARD_HIGHT 180.0
#define CORNER_RADIUS 12.0 //让界面变得更好看的魔法
- (CGFloat)cornerScaleFactor:(CGFloat)height {return height / CORNER_FONT_STANDARD_HIGHT;}
- (CGFloat)cornerRadius:(CGFloat)height {return CORNER_RADIUS * [self cornerScaleFactor:height];} - (void)viewDidLoad
{
//又是个让界面变得更好看的魔法
self.restartButton.layer.cornerRadius = [self cornerRadius:self.restartButton.bounds.size.height];
self.gameModelSelectSegmented.layer.cornerRadius = [self cornerRadius:self.gameModelSelectSegmented.bounds.size.height * ]; [self.gameModelSelectSegmented addTarget:self action:@selector(segmentAction:) forControlEvents:UIControlEventValueChanged];//target-action _selfDefiningModel = ;//default model
} - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"show PlayingCardGameHistory"])
{
if ([segue.destinationViewController isKindOfClass:[HistoryViewController class]])
{
HistoryViewController *HVC = (HistoryViewController *)segue.destinationViewController;
HVC.history = self.game.gameStateHistory;
}
}
} - (void) segmentAction:(UISegmentedControl *)Seg
{
if (self.gameModelSelectSegmented.selectedSegmentIndex == )
{
[self assertSelfDefiningModel:self.matchModelTextFiled.text];
}
else
{
self.selfDefiningModel = self.gameModelSelectSegmented.selectedSegmentIndex + ;
}
self.gameModelLable.text = [NSString stringWithFormat:@"game model:%lu",(unsigned long)self.selfDefiningModel];
} - (void) assertSelfDefiningModel:(NSString *)text
{
if ([self.matchModelTextFiled.text integerValue] < )
{
[[[UIAlertView alloc] initWithTitle:@"Wrong" message:@"game model at least 2" delegate:nil cancelButtonTitle:@"certain" otherButtonTitles:nil, nil] show];
self.matchModelTextFiled.text = @"";
self.gameModelSelectSegmented.selectedSegmentIndex = ;
}
else if ([self.matchModelTextFiled.text integerValue] > [self cardButtonsNumber])
{
[[[UIAlertView alloc] initWithTitle:@"Wrong" message:@"beyond card max number" delegate:nil cancelButtonTitle:@"certain" otherButtonTitles:nil, nil] show];
self.matchModelTextFiled.text = @"";
self.gameModelSelectSegmented.selectedSegmentIndex = ;
}
else
{
self.selfDefiningModel = [self.matchModelTextFiled.text integerValue];
}
} - (Deck *)createDeck
{
return [[PlayingCardDeck alloc] init];
} - (IBAction)touchRestartButton
{
//恢复默认值
self.gameModelSelectSegmented.enabled = YES;
self.matchModelTextFiled.enabled = YES;
self.matchModelTextFiled.enabled = YES;
self.gameModelSelectSegmented.selectedSegmentIndex = ;
self.selfDefiningModel = ;
self.gameModelLable.text = [NSString stringWithFormat:@"game model:%d",_selfDefiningModel];
self.matchModelTextFiled.text = nil; self.game = nil;
[self updateUIWithNotCreateGame];
} - (IBAction)touchCardButton:(UIButton *)sender
{
//游戏开始后禁用模式选择功能
self.gameModelSelectSegmented.enabled = NO;
self.matchModelTextFiled.enabled = NO;
self.matchModelTextFiled.enabled = NO; NSUInteger cardIndex = [self.cardButtons indexOfObject:sender];
if(!self.game)
{
self.game = [[PlayingCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]];
self.game.gameModel = self.selfDefiningModel;
}
[self.game chooseCardAtIndex:cardIndex];
[self updateUI];
} - (void) updateUIWithNotCreateGame
{
for (UIButton *cardButton in self.cardButtons)
{
[cardButton setTitle:@"" forState:UIControlStateNormal];
[cardButton setBackgroundImage:[UIImage imageNamed:@"cardBack"] forState:UIControlStateNormal];
cardButton.enabled = YES;
}
self.gameStateLable.text = @"State";
self.scoreLable.text = @"Score:0";
} - (void)updateUI
{
[super updateUI];
self.gameStateLable.text = ((PlayingCardMatchingGame *)self.game).gameState;
} - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
} @end
SetCardGameViewController
本类实现了SetCard匹配游戏,因为是UI层,因此使用了NSAttributeString来表示SetCard的内容,同样也实现了History功能
注意:由于SetCard牌面始终朝上,因此要在ViewDidLoad中初始化game
#import "ViewController.h" @interface SetCardGameViewController : ViewController @end
#import "SetCardGameViewController.h"
#import "SetCardMatchingGame.h"
#import "SetCardDeck.h"
#import "SetCard.h"
#import "HistoryViewController.h" @interface SetCardGameViewController() @property (weak, nonatomic) IBOutlet UILabel *gameStateLable;
@property (weak, nonatomic) IBOutlet UIButton *restartButton;
@property (nonatomic) NSUInteger selfDefiningModel;
@property (nonatomic,strong) NSDictionary *colorOfCard; @end @implementation SetCardGameViewController #define CORNER_FONT_STANDARD_HIGHT 180.0
#define CORNER_RADIUS 12.0 //让界面变得更好看的魔法
- (CGFloat)cornerScaleFactor:(CGFloat)height {return height / CORNER_FONT_STANDARD_HIGHT;}
- (CGFloat)cornerRadius:(CGFloat)height {return CORNER_RADIUS * [self cornerScaleFactor:height];} - (void)viewDidLoad
{
//又是个让界面变得更好看的魔法
self.restartButton.layer.cornerRadius = [self cornerRadius:self.restartButton.bounds.size.height]; self.colorOfCard = @{@"red" : [UIColor redColor], @"green" : [UIColor greenColor], @"purple" : [UIColor purpleColor]}; _selfDefiningModel = ;//default model if(!self.game)
{
self.game = [[SetCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]];
self.game.gameModel = self.selfDefiningModel;
}
[self updateUIWithNotCreateGame];
} - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"show SetCardGameHistory"])
{
if ([segue.destinationViewController isKindOfClass:[HistoryViewController class]])
{
HistoryViewController *HVC = (HistoryViewController *)segue.destinationViewController;
HVC.history = self.game.gameStateHistory;
}
}
} - (Deck *)createDeck
{
return [[SetCardDeck alloc] init];
}
- (IBAction)touchCardButtons:(UIButton *)sender
{
//游戏开始后禁用模式选择功能
NSUInteger cardIndex = [self.cardButtons indexOfObject:sender];
if(!self.game)
{
self.game = [[SetCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]];
self.game.gameModel = self.selfDefiningModel;
}
[self.game chooseCardAtIndex:cardIndex];
[self updateUI];
} - (IBAction)touchRestartButton:(UIButton *)sender
{
//恢复默认值
self.selfDefiningModel = ;
self.game = nil;
if(!self.game)
{
self.game = [[SetCardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]];
self.game.gameModel = self.selfDefiningModel;
}
[self updateUIWithNotCreateGame];
} - (void) updateUIWithNotCreateGame
{
for (UIButton *cardButton in self.cardButtons)
{
NSUInteger cardIndex = [self.cardButtons indexOfObject:cardButton];
Card *card = [self.game cardAtIndex:cardIndex];
[cardButton setAttributedTitle:[self attributeTitleForCard:card] forState:UIControlStateNormal];
[cardButton setBackgroundImage:[UIImage imageNamed:@"cardFront"] forState:UIControlStateNormal];
cardButton.enabled = YES;
}
self.gameStateLable.text = @"State";
self.scoreLable.text = @"Score:0";
} - (void)updateUI
{
for (UIButton *cardButton in self.cardButtons)
{
NSUInteger cardIndex = [self.cardButtons indexOfObject:cardButton];
Card *card = [self.game cardAtIndex:cardIndex];
[cardButton setAttributedTitle:[self attributeTitleForCard:card] forState:UIControlStateNormal];
[cardButton setBackgroundImage:[self backgroundImageForCard:card] forState:UIControlStateNormal];
cardButton.enabled = !card.isMacthed;
}
self.scoreLable.text = [NSString stringWithFormat:@"Score:%ld",(long)self.game.score];
self.gameStateLable.text = ((SetCardMatchingGame *)self.game).gameState;
} - (NSAttributedString *)attributeTitleForCard:(Card *)card
{
NSShadow *shadow1 = [[NSShadow alloc] init];
shadow1.shadowColor = [UIColor grayColor];
shadow1.shadowOffset = CGSizeMake(2.0f, 2.0f); NSShadow *shadow2 = [[NSShadow alloc] init];
shadow2.shadowColor = [UIColor whiteColor];
shadow2.shadowOffset = CGSizeMake(0.0F,0.0f);
return [[NSAttributedString alloc] initWithString:((SetCard *)card).suit
attributes:@{NSForegroundColorAttributeName : [self.colorOfCard valueForKey:((SetCard *)card).color],
NSShadowAttributeName : ((SetCard *)card).shading ? shadow1 : shadow2,
NSFontAttributeName : [UIFont boldSystemFontOfSize:10.0f]}];
} - (UIImage *)backgroundImageForCard:(Card *)card
{
return card.chosen ? [UIImage imageNamed:@"setCardback"] : [UIImage imageNamed:@"cardFront"];
} @end
d.history功能的实现
HistoryViewController
属性history用于接收父MVC传来的数据
#import <UIKit/UIKit.h> @interface HistoryViewController : UIViewController
@property (strong, nonatomic) NSArray *history;
@end
#import "HistoryViewController.h" @interface HistoryViewController ()
@property (weak, nonatomic) IBOutlet UITextView *HistoryTextView;
@end @implementation HistoryViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
for (NSString *state in self.history)
{
self.HistoryTextView.text = [self.HistoryTextView.text stringByAppendingFormat:@"%@\n",state];
}
}
@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课程
(6/18)重学Standford_iOS7开发_控制器多态性、导航控制器、选项卡栏控制器_课程笔记的更多相关文章
- (1/18)重学Standford_iOS7开发_iOS概述_课程笔记
写在前面:上次学习课程对iOS还是一知半解,由于缺乏实践,看公开课的视频有时不能很好地领会知识.带着问题去学习永远是最好的方法,接触一段时间iOS开发以后再来看斯坦福iOS公开课,又会有许多新的发现, ...
- (5/18)重学Standford_iOS7开发_视图控制器生命周期_课程笔记
第五课: 1.UITextView @property (nonatomic, readonly) NSTextStorage *textStorage;//注意为只读属性,因此不能直接更改内容,NS ...
- (8/18)重学Standford_iOS7开发_协议、block、动画_课程笔记
第八课: 1.协议 另一种安全处理id类型的方式如:id <MyProtocol> obj a.声明 //协议一般放于.h文件中或者在类的.h文件中 @protocol Foo <X ...
- (7/18)重学Standford_iOS7开发_视图、绘制、手势识别_课程笔记
第七课: 1.View 一般来说,视图是一个构造块,代表屏幕上一块矩形区域,定义了一个坐标空间,并在其中绘制及添加触控事件等. ①视图的层级关系 一个视图只能有一个父视图,可以有多个子视图 - ( - ...
- (9/18)重学Standford_iOS7开发_动画、自动布局_课程笔记
最近开始实习,没多少时间更新了=_= 第九课: 1.上节课demo:Dropit完整实现 https://github.com/NSLogMeng/Stanford_iOS7_Study/commit ...
- (4/18)重学Standford_iOS7开发_框架和带属性字符串_课程笔记
第四课(干货课): (最近要复习考试,有点略跟不上节奏,这节课的内容还是比较重要的,仔细理解掌握对今后的编程会有很大影响) 本节课主要涉及到Foundation和UIKit框架,基本都是概念与API知 ...
- (3/18)重学Standford_iOS7开发_Objective-C_课程笔记
第三课: 本节课主要是游戏实现的demo,因此我将把课程中简单的几个编程技巧提取出来,重点介绍如何自己实现作业中的要求. 纸牌游戏实现: ①游戏的进行是模型的一部分(理解什么是模型:Model = W ...
- (2/18)重学Standford_iOS7开发_Xcode_课程笔记
第二课: 1.惰性初始化 -(ObjectType *)example { f(!_example) example =[[ObjectType alloc] init]; return _examp ...
- 选项卡栏控制器(UITabBarController)
选项卡栏控制器管理的每个场景都包含一个UITabBarItem,它包含标题.图像和徽章. 在场景里可以通过tabBarItem属性来获得UITabBarItem的引用.例如:[self.tabBarI ...
随机推荐
- CGDataCmd
1,"Get Inf Joint from file" 选择文件中储存的骨骼信息; 2,"Export skinWeight" 导出权重; 3," ...
- 转:如何制作一个定制的 PHP 基础 Docker 镜像(一)
原文来自于:http://open.daocloud.io/ru-he-zhi-zuo-yi-ge-ding-zhi-de-php-ji-chu-docker-jing-xiang/ 目标:准备一个定 ...
- Java多线程初学者指南(4):线程的生命周期
与人有生老病死一样,线程也同样要经历开始(等待).运行.挂起和停止四种不同的状态.这四种状态都可以通过Thread类中的方法进行控制.下面给出了Thread类中和这四种状态相关的方法. // 开始线程 ...
- csu 10月 月赛 D 题 CX and girls
Description CX是要赶去上课,为了不迟到必须要以最短的路径到达教室,同时CX希望经过的路上能看到的学妹越多越好.现在把地图抽象成一个无向图,CX从1点出发,教室在N号点,告诉每个点上学妹的 ...
- 【HDU 4898】 The Revenge of the Princess’ Knight (后缀数组+二分+贪心+...)
The Revenge of the Princess’ Knight Problem Description There is an old country and the king fell in ...
- FFMPEG之TimeBase成员理解
http://blog.csdn.net/supermanwg/article/details/14521869
- Aptana Studio 安装
Aptana Studio 是一个集成式的Web应用程序开发环境,它不仅可以作为独立的程序运行,而且还可以作为Eclipse插件使用. AptanaStudio是一个基于Eclipse的集成式Web开 ...
- wcf中netTcpBinding的元素构成
<security> of <netTcpBinding> <transport> of <netTcpBinding> <message> ...
- MySQL源码 数据结构hash
MySQL源码自定义了hash表,因为hash表具有O(1)的查询效率,所以,源码中大量使用了hash结构.下面就来看下hash表的定义: [源代码文件include/hash.h mysys/has ...
- 改善C#程序的50种方法
为什么程序已经可以正常工作了,我们还要改变它们呢?答案就是我们可以让它们变得更好.我们常常会改变所使用的工具或者语言,因为新的工具或者语言更富生产力.如果固守旧有的习惯,我们将得不到期望的结果.对于C ...