斯坦福大学ios公开课笔记(6)--多态与多个MVC的使用

这节课主要介绍了多态的概念和使用,还有多个MVC使用,在这之中介绍了UINavigationController和UITabbarController的使用。

多态

多态是面向对象程序设计中类的三大特征之一,另外两个是继承和封装。objective-c是一个面向对象的开发语言,所以了解如何使用多态是非常有必要的。

在将多态的时候有一个概念不得不提—抽象。

抽象

抽象表示这个类不能被实例化使用,他只能用于作为具体类的超类。简单来说抽象就相当于概念车。我们把车具体化给他四个轮子,他可能就是汽车🚗,给他两个轮子,他可能就是自行车🚲。这就是抽象。

如何实现

这里我们对之前写的Machismo纸牌配对游戏进行了修改,不了解这个游戏的请先看这里,使得它可以对任何种类的卡牌进行配对游戏。

首先,我们之前的代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (CardMatchingGame *)game
{
if(!_game)
{
_game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count] usingDeck:[self createDeck]];
}
return _game;
}
- (Deck *)createDeck
{
return [[PlayingCardDeck alloc] init];
}

这里的return [[PlayingCardDeck alloc] init];代表我们只会返回PlayingCardDeck这个牌堆中的排,如果想要把他抽象,我们首先要把这里改成return nil;。这样他就变成了一个抽象类。

然后我们将- (Deck *)createDeck;这个方法放置到.h公有文件中,并且写一些注释来说明他就是这个抽象类具体化时所需要用到的方法。

1
2
//用来返回不同种类的卡牌
- (Deck *)createDeck; //abstract

接下来我们新建一个controller继承之前抽象好的控制器,然后重写createDeck方法,返回正确的卡牌,就完成了我们的多态。这样,我们可以创建很多控制器返回不同的卡牌类型,并且使用同一套逻辑进行游戏。

1
2
3
4
5
#import "ViewController.h"
@interface PlayingCardViewController : ViewController
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#import "PlayingCardViewController.h"
#import "PlayingCardDeck.h"
@interface PlayingCardViewController ()
@end
@implementation PlayingCardViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (Deck *)createDeck
{
return [[PlayingCardDeck alloc] init];
}
@end

对了,这里要注意,如果你是使用storyboard创建的界面,别忘了把控制器的Class改成刚刚具体化的类。

多个MVC协作

UINavigationController

UINavigationController导航栏,它本身也算作是一个MVC,他的view就是顶部的导航栏。他有一个rootViewController作为输出口指向其他MVC。

导航栏拥有一个自己的容器,这个容器使用的是栈结构,每次我们在执行进入下一个页面或者返回到上一个页面的时候都是一个栈空间压入和弹出的功能的执行。

每次我们压入新的MVC的时候,系统都会从storyboard生成一个新的文件,然后显示出来。而当我们弹出的时候,页面消失,这部分也会被释放掉。这时候如果你有需要持久化使用的数据,你就要把他保存起来。

如何压入一个MVC

答案是使用segue。他是storyboard和Xcode之间唯一联系的地方,我们通过segue的标示符来判断跳到哪一个MVC。

如何弹出一个MVC

通过方法

1
[self.navigationController popViewControllerWithAnimated:YES];

就可以弹出当前的MVC

如何使用segue

我们需要让Xcode知道当我要调到下一个页面的时候我选择的是什么类型的东西,比如我们要从日历中的月份跳到日期的时候,我们需要知道我点击的是几号的日期。

1
2
3
4
5
6
7
8
9
10
11
12
- (void)prepareForSegue:(UIStroyboardSegue *)segue sender: (id)sender
{
if ([segue.identifier isEqualToString:@"DoSomething"])
{
if ([segue.destinationViewController isKindOfClass:[DoSomethingVC class]]){
DoSomethingVC *doVC = (DoSomethingVC *)segue.desitinationViewController;
doVC.infoString = ...
}
}
}

多个MVC协作的Demo

这里我们使用到了上节课使用的项目Attributor.在这次的课中我们给他添加一个新的MVC,并要求这个MVC可以获得上一个MVC的数据然后分析出有多少个字符改变了颜色或者多少个字符添加了边框。

在storyboard中我们使用导航栏来控制各个MVC

logo

设置页面传值

如果想要分析上一个页面中设置字符的颜色,就一定要拿到上一个页面中修改过的字符串信息。这里就要用到页面传值的功能。页面传值简单来说就是在头文件中添加一个公有的属性,然后在跳转页面的时候讲值传给他。

1
2
3
4
5
@interface TextStatsViewController : UIViewController
@property (nonatomic ,strong) NSAttributedString *textToAnalyze;
@end
1
2
3
4
5
6
7
8
9
10
11
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:@"Analyzer Text"])
{
if([segue.destinationViewController isKindOfClass:[TextStatsViewController class]])
{
TextStatsViewController *tsvc = (TextStatsViewController *)segue.destinationViewController;
tsvc.textToAnalyze = self.body.textStorage;
}
}
}

处理字符串

处理字符串主要是把上个页面穿过来的字符串通过NSRange一个一个字符的分析,看看他有没有使用颜色变化或者边框变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- (NSAttributedString *)chaactersWithAttributtr:(NSString *)attributName
{
NSMutableAttributedString *characters = [[NSMutableAttributedString alloc] init];
int index = 0;
while (index < [self.textToAnalyze length])
{
NSRange range;
id value = [self.textToAnalyze attribute:attributName atIndex:index effectiveRange:&range];
if(value)
{
[characters appendAttributedString:[self.textToAnalyze attributedSubstringFromRange:range]];
index = range.length + range.location;
}
else
{
index++;
}
}
return characters;
}
1
2
3
4
5
6
7
- (void)updateUI
{
self.colorfulCharachersLabel.text = [NSString stringWithFormat:@"%ld colorful characters",[[self chaactersWithAttributtr:NSForegroundColorAttributeName] length]];
self.outletCharachersLabel.text = [NSString stringWithFormat:@"%ld outline characters",[[self chaactersWithAttributtr:NSStrokeWidthAttributeName] length]];
}

写好之后就可以统计上一个页面的字符串啦。

logo