承接上一节
简单介绍一下属性的另外两个特性:
首先,在创建make时
@property NSString *make;
默认的是读写形式
我们可能不希望汽车对象创建后能够修改make、model或year,这时候我们可以在声明属性的时候将其声明为只读属性
方法很简单
@property (readonly) NSString *make;
这样就声明完成了,之后当我们在ViewController.m文件中试图对创建完成的myCar或者是othercar对象,试图修改其make值的时候,
系统会报错
属性声明的一般形式如下:
@property <(qualifier1,qualifier2,...)> <type> <property_name>
另外,如果我们希望实现这些属性在外部只读,而在内部可以读写。也很简单,因为可以在.m文件中重新声明属性。可以通过在Car.m文件的
@implementation语句之前增加如下代码,添加或覆盖类的接口定义:
@interface Car()
@property (readwrite) int year;
@property NSString *make;
@property NSString *model;
@end
Car对象会使用新的实例变量定义。注意明确指定readwrite并不是必须的,默认值是readwrite。所以上面代码中make与model跟year是一样的,重定义后变成内部可读写。
Objective-C的语法之类的就不再多说了
现在介绍故事面板
(来自书上)对于用户来说,界面就是我们的应用程序。它是用户与为应用程序提供动力的功能(即逻辑和数据)之间的主要门户。通过创建一些信息型和实用性元素,包括屏幕和这些屏幕上所显示的内容,可以设计与实现用户体验。我们所设计的每个屏幕可以实现如下功能:
- 显示信息,如CarValet应用程序中汽车对象的数量
- 支持动作,如通过触摸按钮创建一辆新汽车
- 请求输入,如制定汽车的品牌
- 显示用户活动的结果,其方式包括更新所展示的汽车,或者切换至一个支持用户编辑汽车的屏幕
用户每次使用应用程序时,就像是在体验故事。故事所关注的内容可能是生活管理(Calender应用程序);与朋友联系(邮件、社交应用程序等)
根据我们一开始写的Carvalet项目,我们为他搭建场景
到这个地方来
改成——Headline
其余的改变:
可视化元素 | 作用 | 类名 |
---|---|---|
Total Cars | 显示停泊的汽车总数–Car对象的数量 | UILabel |
New Car | 通过添加新的Car对象,停泊一辆车 | UIButton |
Divider | 在增加汽车和查看汽车两块区域间添加一条视觉分割线,不会的话下面有图解 | UIView |
Current Car Number | 显示当前所展示的汽车对象的索引数 | UILabel |
Car Information | 显示当前汽车的详情 | UILabel |
Previous | 让泊车员浏览汽车,提供前一辆汽车的详情(如果有车) | UIButton |
Next | 提供下一辆车的详情(如果有车) | UIButton |
运行如图:(PS:New Car一开始我弄成Label,应该是Button,上面图截得New Car颜色应该与Previous,Next颜色一样,这里我就不换图了)
好了,到目前为止,我们的界面做出来了,但是只有外表,内在啥也没有,我们现在来添加的这些元素行为
添加action和outlet
元素 | 类型 | 名称 |
---|---|---|
Total Car标签 | IBOutlet | totalCarsLabel |
Car NUmber标签 | IBOutlet | CarNumberLabel |
Car Info标签 | IBOutlet | CarInfoLabel |
New Car按钮 | IBAction | newCar: |
Previous Car按钮 | IBAction | previousCar: |
Next Car按钮 | IBAction | nextCar: |
方法如图
拉过去后会跳出,对于label选择Connection方式为Outlet,Name根据上表
weak表示对象的所有权,能够帮助编译器添加内存管理代码。理解这些内存限定符的意义非常重要。
button也是用同样的方法拉过去,不过跳出的对话框中要改成下图样式,Name根据上表
这是添加完6个元素后
你会发现在ViewController.m里面,自动多出了点东西,那些是按钮时间,每个按钮如何操作都在相对应的按钮方法里面实现
对于内存的介绍的话本文不多介绍,想了解更多自行谷歌(在这里推荐一个翻墙的东东,Star VPN,可以在App Store里面下载)
在ViewController.m
@implementation ViewController {
NSMutableArray *arrayOfCars; //使用mutable array记录所有汽车对象
NSInteger displayedCarIndex; //指定靠下位置显示的汽车的数组索引
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
arrayOfCars = [[NSMutableArray alloc] init];//初始化汽车的数组为空数组
displayedCarIndex = 0;//显示创建的第一辆汽车
}
好了现在有保存新车的地方了,还需要创建它们,创建的方法是newCar:
newCar:方法
- (IBAction)newCar:(id)sender {
Car *newCar = [[Car alloc] init];//用默认值创建新的Car对象,并将其添加到数组中
[arrayOfCars addObject:newCar];//添加
NSString *totalCarText;
totalCarText = [NSString stringWithFormat:@"Total Cars: %d",[arrayOfCars count]];//基于当前的汽车数量,创建新的total Car Text字符串
self.totalCarsLabel.text = totalCarText;//更新显示泊车员字符串
}
运行一下,点击new Car
这里我们会注意到,之前是999,我们只要到故事面板把他改成0就行了
加入显示汽车行为
显示对象的信息是另一种常见的任务。UILabel能显示字符串,通过界面中的text属性,可以使用任何字体、大小以及指定颜色。当text属性有所更改时,标签也会更新。
使用UILabel显示变化的信息需要4样东西:标签、标签的关联、用于显示的字符串以及设置标签文本的代码。
我们已经在场景中添加好汽车的信息标签,也已经关联到标签上。要添加对汽车的描述有几种方法:一种是通过添加公共方法,另一种是使用read-only属性和自定义的getter方法。要实现该属性和getter方法,可以参照以下步骤:
- 在Car.h文件中,在fuelAmount的下方添加这行声明属性的代码
@property (readonlu)NSString *carInfo;
- 打开Car.m文件,在initWithMake:的下方添加实现自定义getter方法的代码
- (NSString *)carInfo {
return [NSString stringWithFormat:
@"Car Info\n Make: %@\n Model: %@\n year: %d",self.make ? self.make :@"Unknown Make",self.model ? self.model :@"Unknown Model",self.year];
}
PS 三元运算符?,不懂自行谷歌
现在,可以获得描述汽车信息的字符串,还需要一种方法,用于更新显示汽车数量和信息的标签的文本内容。可以一一设置每个标签所要更新的信息。但是,更新显示的汽车信息这种行为会在多个地方发生。在这种情况下,抽出相关的代码是良好的设计方式
在newCar:方法的上端,添加displayCurrentCarInfos方法
- (void)displayCurrentCarInfo {
Car *currentCar;
currentCar = [arrayOfCars objectAtIndex:displayedCarIndex];
//对当前显示的汽车对象进行加载。注意生产代码应该核对displayedCarIndex是否为有效索引
self.CarInfoLabel.text = currentCar.carInfo;
//通过carInfo属性获得汽车的描述信息
NSString *carIndexText;
//更新汽车的标签,注意将当前索引加1(数组从0开始)
carIndexText = [NSString stringWithFormat:@"Car Number: %d",displayedCarIndex +1];
self.CarNumberLabel.text = carIndexText;
}
在newCar:方法中,调用displayCurrentCarInfo
- (IBAction)newCar:(id)sender {
Car *newCar = [[Car alloc] init];//用默认值创建新的Car对象,并将其添加到数组中
[arrayOfCars addObject:newCar];//添加
NSString *totalCarText;
totalCarText = [NSString stringWithFormat:@"Total Cars: %d",[arrayOfCars count]];//基于当前的汽车数量,创建新的total Car Text字符串
[self dispalyCurrentCarInfo];
self.totalCarsLabel.text = totalCarText;//更新显示泊车员字符串
}
运行结果
发现只有”Car Info”只有一行,这是因为我们在创建Car Information时,只弄了一行
在故事面板中如下修改(就是拉长拉宽了)
再次运行
现在操作上一辆跟下一辆
在这里先写一个changeDisplayedCar(用来令车信息改变)
因为上一辆下一辆的实质上是改变当前显示的信息
- (void)changeDisplayedCar:(NSInteger)newIndex {
if(newIndex < 0) { //确保新的索引值是有效的索引。如果索引小于0,使值为0
newIndex = 0;
} else if(newIndex >= [arrayOfCars count]) {//如果newIndex超出arrayOfCar的范围,就将其设置为最后一个
newIndex = [arrayOfCars count] - 1;
}
if (displayedCarIndex != newIndex) {//仅更新索引值有变化的视图
displayedCarIndex = newIndex;
[self displayCurrentCarInfo];
}
}
previousCar:和nextCar:方法如下
- (IBAction)previousCar:(id)sender {
[self changeDisplayedCar:displayedCarIndex - 1];
}
- (IBAction)nextCar:(id)sender {
[self changeDisplayedCar:displayedCarIndex + 1];
}
运行结果
- (void)updateLabel:(UILabel*)theLabel
withBaseString:(NSString*)baseString
count:(NSInteger)theCount {
NSString *nexText;
nexText = [NSString stringWithFormat:@"%@: %d",baseString,theCount];
theLabel.text = nexText;
}
下面这些代码是让newCar和显示车辆信息通过一个专门的更新标签的方法来实现标签的更新,这样就不用每一个标签写一个专门的更改的方法了
- (void)displayCurrentCarInfo {
Car *currentCar;
currentCar = [arrayOfCars objectAtIndex:displayedCarIndex];
//对当前显示的汽车对象进行加载。注意生产代码应该核对displayedCarIndex是否为有效索引
self.CarInfoLabel.text = currentCar.carInfo;
//通过carInfo属性获得汽车的描述信息
[self updateLabel:self.CarNumberLabel withBaseString:@"Car Number" count:displayedCarIndex +1];
}
- (IBAction)newCar:(id)sender {
Car *newCar = [[Car alloc] init];//用默认值创建新的Car对象,并将其添加到数组中
[arrayOfCars addObject:newCar];//添加
[self updateLabel:self.totalCarsLabel withBaseString:@"Total Cars" count:[arrayOfCars count]];
}
今天的介绍就到这里咯
我的另一个博客站点:Arnold-你们好啊