IOS开发入门(5)

承接上节

在本节中,将对之前的故事版进行改进

改进故事版I

先大致介绍一下协议

在大多数应用程序中,都会存在一些需要与其他对象中的触发行为者进行交换的数据,这导致互相关联、不独立的代码难以维护和复用。相应的解决办法,就是使用协议(protocol)。

协议增加了代码的可维护性、复用性和灵活性。可以认为协议是请求者和供应者之间的合同。供应者常被叫做代理(delegate),统一实现一些方法,从而请求者可以调用。每个方法通常用于如下三个目的之一

  • 从代理请求数据,如Car对象
  • 在改变时通知代理,以允许存储、取消或再次显示数据
  • 指向特定的行为,如呼叫某人前来取车

协议的主要优点在于复用性,并且每个实现了所规定的方法的类,都可以成为代理。相似的是,任何类都可以使用协议方法,请求信息或发起行为。

要了解更多关于协议的信息,查看Programming in Objective-C 2.0,第二版,Stephen G Kochan著,有Addison We是可以出版,翻看该书中第231页关于协议的内容。也可以阅读苹果公司Objective-C文档中关于协议的章节。

使用协议交换数据

协议的常见用法是交换数据。编辑者或请求者需要信息,由代理提供并且或许由代理更新。

在这种情况下,汽车编辑者需要知道汽车对象和拍照。Add/View场景或代理,在汽车对象有改变时需要被通知到,从而可以更新显示内容。这要求协议有三个方法:

  • carToEdit返回汽车对象以进行编辑
  • carNumber返回被编辑汽车的拍照(不是索引数)
  • editedCarUpdated告诉代理,编辑操作已经完成

现在创建新的Objective-C协议,命名为CarEditViewControllerProtocol,并将它添加到项目中。

需要注意的是,需要选择Objective-C File中,File Type 选择protocol

代码如下:

#import <Foundation/Foundation.h>//在协议中个导入用到的类的所有头文件。Foundation.h定义了许多Cocoa类型,包括NSInteger类型

@class Car;

@protocol CarEditViewControllerProtocol <NSObject>//@protocol是声明协议的指令。接下来的部分是协议的名称,接着是协议要包含的内容。在这种情况下,CarEditViewControllerProtocol有权限访问任何在NSObject协议中声明的方法,如selfclassdescription

- (Car*)carToEdit;//指定方法声明的方式如同公共类方法

- (NSInteger)carNumber;

- (void)editedCarUpdated;

@end

在定义好协议后,改变要使用该协议的请求者。在CarValet应用中,从改变导入文件开始,进行修改。在CarEditViewController.h文件中,移除carNumber属性,然后按下列代码进行更改

#import <UIKit/UIKit.h>

#import "CarEditViewControllerProtocol.h"//修改

@class Car;

@interface CarEditViewController : UIViewController

//@property (nonatomic) NSInteger carNumber;//修改

@property (weak, nonatomic) id <CarEditViewControllerProtocol> delegate;//修改

@property (strong,nonatomic) Car *currentCar;
@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

最后一个修改是协议中十分重要的一部分。id是占位符,能代表任意类的对象。这意味着任意类,包含哪些我们还没有想到过的类,都可以成为该协议的代理。虽然Edit场景要求Car对象拥有某些特定属性,但我们依然拥有设置该代理的灵活性。可以在其他的项目中重用到Edit场景。另一处要注意的地方是,在声明id变量类型时并不适用星号。id变量的声明应该是delegate而非*delegate

在类型之后,用尖括号括起的CarEditViewControllerProtocol,这表明无论delegate所属的类是什么,他都必须遵从协议。也就是说,它必须实现协议要求的所有方法。

现在更新CarEditViewController.m中的viewDidLoad方法,代码如下

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    NSString *carNumberText;
    carNumberText = [NSString stringWithFormat:@"Car Number:%d",[self.delegate carNumber]];//修改 启辰的拍照,现在从代理返回
    self.carNumberLabel.text = carNumberText;

    self.currentCar = [self.delegate carToEdit];//修改 因已编辑过Car对象,故而通知代理。
    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];

最后一处修改是通知Add/View场景进行自我更新。在viewWillDisappear:的结尾处添加对[self.delegate editedCarUpdated]的调用

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    [self.delegate editedCarUpdated];
}

现在已经完成请求者部分,是时候更新代理部分了。

修改ViewController.h文件顶部的代码

#import <UIKit/UIKit.h>

#import "CarEditViewControllerProtocol.h"//修改

@interface ViewController : UIViewController

<CarEditViewControllerProtocol>//修改

@property (weak, nonatomic) IBOutlet UILabel *totalCarsLabel;
@property (weak, nonatomic) IBOutlet UILabel *CarNumberLabel;
@property (weak, nonatomic) IBOutlet UILabel *CarInfoLabel;

- (IBAction)newCar:(id)sender;
- (IBAction)previousCar:(id)sender;
- (IBAction)nextCar:(id)sender;

@end

<CarEditViewControllerProtocol>的意思是ViewController支持——也即是说,遵从——我们的协议。当然,我们还未实现任何支持。此处,Xcode展现了它帮助你检查代码完整的方法之一。查看工具栏中间部分的状态栏,可以看到右边有黄色警告三角形。表明在编译代码时已导致警告。单击警告可以找到更多细节。因为协议的方法部分不存在,该警告表明存在不完整的实现。

在newCar:方法的上方添加协议方法:

- (Car*)carToEdit {
    return arrayOfCars[displayedCarIndex];
}

- (NSInteger)carNumber {
    return displayedCarIndex + 1;
}

- (void)editedCarUpdated {
    [self displayCurrentCarInfo];
}

最后还有一个错误提示需要解决

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if([segue.identifier isEqualToString:@"EditSegue"]){
        CarEditViewController *nextCOntroller;

        nextCOntroller = segue.destinationViewController;
        nextCOntroller.delegate = self;//修改
        Car *currentCar = arrayOfCars[displayedCarIndex];
        nextCOntroller.currentCar = currentCar;
    }
}

然后就是成功运行了。

改进故事版2

IOS 6 引入了一种更好的方法来将数据返回到激发segue的视图控制器:简单地将一个特殊类型的IBAction添加到视图控制器中即可。同城,动作方法有个看似为(id)sender的参数,或者根本没有参数。通过将参数修改为(UIStoryboard*)segue,可以创建特殊类型的动作——一种能接受segue对象的动作。

仅当一个场景激发另一个场景的切换时才会发送prepareForSegue:sender:。它是向前segue。在IOS 6中,存在另一种类型的segue,其动作是相反的:在一个changing想要返回到之前的场景时发送prepareForSeguesender:。它会回退(unwinds)首次打开场景的segue,并且在打开之前的场景时不会创建新场景。

要查看这个回退动作,修改如下代码:
在ViewController.m底部添加

- (IBAction)editingDone:(UIStoryboardSegue*)segue {
    [self displayCurrentCarInfo];
}

现在需要激发这个回退动作,方法如同Edit的修改

添加按钮(这里换了个样式)

这里写图片描述

Ctrl将Done拉到下图位置,选择editingDone
这里写图片描述

在CarEditViewController.m中修改如下方法

- (IBAction)editingDone:(UIStoryboardSegue*)segue {
    NSLog(@"\neditedCarUpdated called!\n");
    [self displayCurrentCarInfo];
}

每次Done之后,会在控制台看到
列表内容

运行的时候发现,还是没能立即刷新,然而你对同一个再次编辑,再次返回,会发现他保存的是上一个的变化。

讲道理,我们要的应该是点返回只有立即更新才对,那么做如下修改
在CarEditViewController.m中添加方法,这个跟之前跳转到类似,我就不多解释了

添加segue标签
这里写图片描述

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"EditDoneSegue"]){
        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];
    }
}

现在运行之后,一旦更更久能立即显示啦

今天的介绍就到这里咯

我的另一个博客站点:Arnold-你们好啊

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值