承接上节
在本节中,我们将联系应用程序开发中的另一项核心功能:布局和给场景写代码,然后构建新场景与现有界面之间的关联。对于编辑场景的部分,需要在故事面板中添加新的场景,增加用于显示与编辑汽车的视图,并创建该场景中的自定义控制器。接下来,实现到达和离开View/Edit场景的转场动画。
如果使用该应用程序的泊车员只停某一类型的车,那么现有功能已经做够了。然而,汽车有不同的品牌、类型和年份,因此该应用程序还需要帮助泊车员用这些标准辨别汽车。
所以,我们需要添加一个用来编辑汽车的场景:
添加新场景的过程如下:
- 创建一个管理新场景的对象,这里采用的是IOS系统内置的视图控制器的一个子类
- 往故事板中加入一个同样的系统内置类型的IOS视图控制器
- 将这个视图新控制器的类名设置为步骤1中的控制器名
- 往故事板的新场景内添加可视化元素
- 创建场景所需要的action或outlet
- 编写代码,使其能够工作,并确保工作正常
在这个过程中,有时还需要添加一些所需要的过渡动画。
好了,书上的说明哔哔完了,进入实践模式
首先添加新的场景:
创建完之后,类似于之前,也就是上图左侧的那个面板
然后我们要把测试的故事面板换一下
把下图的箭头用鼠标拖过去
Car Number字体设置成Headline
Make:那些是Label,后面跟着的是Text Field
元素 | 属性名称 |
---|---|
Car Number | carNumberLabel |
Make文本框 | makeField |
Model文本框 | modelField |
Year文本框 | yearField |
Fuel文本框 | fuelField |
将这些添加到CarEditViewController.h中,忘了的话可以看前面的章节
结果如图:
然后现在添加公共属性
#import <UIKit/UIKit.h>
@class Car;//添加代码
@interface CarEditViewController : UIViewController
@property (nonatomic) NSInteger carNumber;//添加代码 @class表明向前引用
@property (strong,nonatomic) Car *currentCar;//添加代码 控制器使用strong内存限定符,因为需要确保被编辑的汽车对象不会从内存中移除
@property (weak, nonatomic) IBOutlet UILabel *carNumberLabel;
@property (weak, nonatomic) IBOutlet UITextField *makeField;
@property (weak, nonatomic) IBOutlet UITextField *modelField;
@property (weak, nonatomic) IBOutlet UITextField *yearField;
@property (weak, nonatomic) IBOutlet UITextField *fuelField;
@end
修改CarEditViewController.m文件中的viewDidLoad:方法,这个方法会在每次显示车辆信息的时候调用
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSString *carNumberText;
carNumberText = [NSString stringWithFormat:@"Car Number:%d",self.carNumber];
self.carNumberLabel.text = carNumberText;
self.makeField.text = self.currentCar.make;
self.modelField.text = self.currentCar.model;
self.yearField.text = [NSString stringWithFormat:@"%d",self.currentCar.year];
self.fuelField.text = [NSString stringWithFormat:@"%0.2f",self.currentCar.fuelAmount];
}
下面的viewWillDIsppear:方法会在每次视图消失的时候用,因为我们可能会修改原来的数据,所以该方法会在切换的时候将当前界面显示的东西覆盖到原来的数据(如果没变动话,原来数据被旧数据覆盖;变动的话,原来数据被新数据覆盖)
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
self.currentCar.make = self.makeField.text;
self.currentCar.model = self.modelField.text;
self.currentCar.year = [self.yearField.text integerValue];
self.currentCar.fuelAmount = [self.fuelField.text floatValue];
}
运行结果如图
讲道理,这里的Year和Fuel的输入框应该是数字才对,这里竟然是字符
这是我们的Keyboard设置的不对
如下修改:
对year
将Keyboard Type改为Number Pad
对fuel
将Keyboard Type改为Decimal Pad
Number Pad与Decimal Pad的不同之处在于,一个的左下角有小数点,另一个没有
再次运行
(PS,这是Number Pad)
(PS,这是Decimal Pad)
好了,这时候我们第二个故事面板搞定了。我们还要做的事就是将之前那个面板跟现在这个面板联系起来
将箭头移回第一个面板
并添加一个Button叫Edit放到图中位置(当然你也可以随便放)
然后就跟着图做(先不用知道为什么,之后会再说的)
发生了什么变化呢?
- Xcode移动首个场景的箭头到插入的导航控制器中(图上没有截到,就是上面我们移动的那个箭头,移动到第二个故事面板之后又移回去的那个)
- 加入的关系链位于控制器和Add/View场景之间
- 导航控制器在场景中添加了一个标题栏(后面两个故事面板上面那灰色的区域)
双击画圈圈的地方,做修改:将Title改成CarValet
运行:
发现Total Car被盖掉了
这时候要把它拉下来
如下图(我这里就不再截图了)
好了,该链接两个故事面板了。按住ctrl,将Edit按钮拖向第二个故事面板,会出现下图的菜单
选中push,结果如下图
这时候运行程序,点击Edit,会发现成功的切换故事面板
在ViewController.m文件中
首先导入CarEditViewController.h文件
import "CarEditViewController.h"
然后添加如下方法,注意看注释
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:@"EditSegue"]){//设置Edit场景,除非这是edit segue
CarEditViewController *nextCOntroller;
nextCOntroller = segue.destinationViewController;//冲这个segue获得Edit场景的视图控制器。segue拥有源控制器属性和目的控制器属性,也就是当前屏幕上显示的控制器(源控制器)与将要显示的控制器(目的控制器)
nextCOntroller.carNumber = displayedCarIndex + 1;//设置car Number属性,机制索引是基于0的
Car *currentCar = arrayOfCars[displayedCarIndex];//使用Objective-C的索引而非objectAtIndex方法
nextCOntroller.currentCar = currentCar;//找到并设置好正确的Car对象
}
}
从一个场景转换到另一个场景时,通常需要传递某些类型的信息。用户去某个其他的定做某事或浏览某物。
上述的方法目的就是,在segue启动之后,而且是在新的场景要调用viewDidLoad:方法之前,将获得的信息传到新的场景。也就是如果我们的车有信息的话,就能把他传到Edit场景中。
这时候还没结束(如果你运行的话,会发现year还是0)
我们需要设置segue的名称,修改如下图
这时候再次运行
发现并做下图修改
返回之后发现并没有变化,然而其实他已经变化了,要查看这一点,你可点击Next到下一辆车再Previous到上辆车,你就会发现变化了
你可能会疑惑,为什么我们不用segue返回?
通知Add/View场景更新,你可能的第一想法是使用segue实现返回。遗憾的是,这点行不通。segue并不能返回那个在Edit场景打开的、与之前相同的Add/View视图控制器实例。要知道,每个segue会创建新的视图控制器,因而它会跳到一个全新的、只有一辆汽车的视图控制器。更糟的是,原来的Edit场景可能已被释放,而随着每个Edit/返回循环将会创建两个或更多的控制器,他们会将系统的内存占完。
至少有三种方法可以用来更新Add/View场景:
- 添加一个名为currentCarUpdated的方法到ViewController中,并将之关联到CarEditViewController中的ViewController对象。在Edit场景消失时,调用更新方法。
- 使用协议让ViewController实现更新
- 使用IOS6中初次出现的unwind segue机制
第一种方法能实现功能,但会极大降低代码的复用性和模式化。每个视图控制器对象需要知道彼此太多信息,因此它们无法分割。使用协议可以消除这个耦合性问题,这点将在接下来的小节中讲到,在这里就不哔哔了。
今天的介绍就到这里咯
我的另一个博客站点:Arnold-你们好啊