ReactiveCocoa-一个你值得关注的东西-例子
一、ReactiveCocoa是什么? ReactiveCocoa是最近在GitHub上非常火的一个框架,是基于响应式编程开发的。ReactiveCocoa 关于响应式编程,这个维基百科有解释:响应式编程 维基百科举的例子是这样: 在代码中有a=b+c,一旦执行后,b或者c变化,除非再次执行一遍,a不会再变化。而响应式编程的思想,就是b或者c变化时,a的值也随之变化。就像Excel表格中,如果在C1中写入公式:=SUM(A1;B1),也就是说无论A1或者B1发生多少变化,C1值总会根据这个公式自己做变化。为什么微软这么屌,因为响应式编程就是最先从微软实验室得出来的。请参考:Reactivite Extension
二、我写这篇博客的目的 因为我用了,发现不仅从上手程度、功能性、逻辑性、易用性上都非常出色。你可以不必须掌握它,因为目前好像还没有产品是用它来开发的,而且这个ReactiveCocoa还在不断的扩展中。 三、举个例子吧 我有这么一个需求: 1、2个输入框,1个按钮 2、第一个输入框只有满足输入:"我爱刘力"后左侧图片显示对勾。 3、只有第一个满足条件了,第二个才可以输入。而且第二个必须输入的内容不和第一个相同,但最后两个字包含“刘力”,左侧的图片才显示对勾。 4、只有前2个输入框都符合条件了,按钮才可以点击enable=YES 5、按钮点击后出来趣味Alert框。
图一 都不满足条件 图2 只有第一个输入框满足条件后,第二个输入框变化placeholder,并可以输入 图3 都符合条件时,按钮可以点击 图4 点击按钮弹出Alert,选择想或者不想 思考:很简单的一个例子。如果我们常规来写的话,我觉得应该是这样的顺序: 1、第一个textfield的代理方法中进行判断,如果符合条件,出现对勾,将第二个textfield替换Placeholder,并可输入。 2、同1,在第二个textfield的代理方法中进行判断,如果符合,将按钮enable置为YES,替换Title和按钮上的颜色 听起来也不是很复杂,但我们看看如果用ReactiveCocoa来写会是如何的? 这里需要指出,ReactiveCocoa引用了信号源这么一个东西,关于这个解释,我直接引用某位大神的说法: ReactiveCocoa是github去年开源的一个项目,是在iOS平台上对FRP的实现。FRP的核心是信号,信号在ReactiveCocoa(以下简称RAC)中是通过RACSignal来表示的,信号是数据流,可以被绑定和传递。 可以把信号想象成水龙头,只不过里面不是水,而是玻璃球(value),直径跟水管的内径一样,这样就能保证玻璃球是依次排列,不会出现并排的情况(数据都是线性处理的,不会出现并发情况)。水龙头的开关默认是关的,除非有了接收方(subscriber),才会打开。这样只要有新的玻璃球进来,就会自动传送给接收方。可以在水龙头上加一个过滤嘴(filter),不符合的不让通过,也可以加一个改动装置,把球改变成符合自己的需求(map)。也可以把多个水龙头合并成一个新的水龙头(combineLatest:reduce:),这样只要其中的一个水龙头有玻璃球出来,这个新合并的水龙头就会得到这个球。 首先,我们需要引入ReactiveCocoa框架,这里我用的CocoaPods来管理的,如果你不知道CocoaPods的话,看看唐巧的博客:使用CocoaPods来做iOS程序的包依赖管理 开始上代码: #import "TableViewViewController.h"#import <ReactiveCocoa/ReactiveCocoa.h> //下面的两张图用在是否符合要求 #define Wrong [UIImage imageNamed:@"checkbox"] #define Right [UIImage imageNamed:@"checkSelected"] @interface TableViewViewController () @property(nonatomic,retain)UITextField *textFieldOne; @property(nonatomic,retain)UITextField *textFieldTwo; //创建2个输入框的信号源 @property(nonatomic,strong)RACSignal *textFieldOneSignal; @property(nonatomic,strong)RACSignal *textFieldTwoSignal; //创建一个合并的信号源 @property(nonatomic,strong)RACSignal *bothTwoTextFieldSignal; @end @implementation TableViewViewController - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; // Uncomment the following line to preserve selection between presentations. // self.clearsSelectionOnViewWillAppear = NO; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return 3; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil]; if (cell == nil) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; cell.selectionStyle = UITableViewCellSelectionStyleNone; if (indexPath.row == 0) { self.textFieldOne = [self creatTextFieldWithView:cell.contentView WithPlaceHolderStr:@"请输入:我爱刘力"]; UIImageView * imageView = [self creatImageViewWithView:self.textFieldOne]; self.textFieldOne.leftView = imageView; //信号创建 self.textFieldOneSignal = [self.textFieldOne.rac_textSignal map:^id(NSString * value) { //返回下面这两个判断的值 return @((value.length>=1)&&([value isEqualToString:@"我爱刘力"])); }]; //map是改变自己的需求 RAC(imageView,image) = [self.textFieldOneSignal map:^id(NSNumber * value) { if (value.boolValue) { return Right; } return Wrong; }]; } if (indexPath.row == 1) { self.textFieldTwo = [self creatTextFieldWithView:cell.contentView WithPlaceHolderStr:@"不能和上一个一样,但最后两字必须含“刘力”"]; UIImageView * imageView = [self creatImageViewWithView:self.textFieldTwo]; self.textFieldTwo.leftView = imageView; //将输入框2的enable属性和输入框1的信号源进行等价
//将输入框2的enable属性和输入框1的信号源进行等价 RAC(self.textFieldTwo,enabled) = self.textFieldOneSignal; [self.textFieldOneSignal subscribeNext:^(NSNumber *x) { if (![x boolValue]) { self.textFieldTwo.enabled = NO; self.textFieldTwo.placeholder = @"第一个不对,就没办法在我这输入,哈哈"; }else { self.textFieldTwo.enabled = YES; self.textFieldTwo.placeholder = @"不能和上一个一样,但最后两字必须含“刘力”"; } }]; //信号创建 self.textFieldTwoSignal = [self.textFieldTwo.rac_textSignal map:^id(NSString * value) { //返回下面这两个判断的值 return @((value.length>=1)&&([value hasSuffix:@"刘力"])&&(![value isEqualToString:@"我爱刘力"])); }]; //map是改变自己的需求 RAC(imageView,image) = [self.textFieldTwoSignal map:^id(NSNumber * value) { if (value.boolValue) { return Right; } return Wrong; }]; } } if (indexPath.row == 2) { //在这个地方去指定合并信号源,因为前两个信号源已经生成 self.bothTwoTextFieldSignal = [RACSignal combineLatest:@[self.textFieldOneSignal,self.textFieldTwoSignal] reduce:^(NSNumber*textFieldOne,NSNumber*textFieldTwo){ //2个合并成一个,如果2个都符合条件,合并信号源也输出YES,否则NO return @(textFieldOne.boolValue&&textFieldTwo.boolValue); }]; UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom]; [button setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted]; button.frame = cell.contentView.bounds; [cell.contentView addSubview:button]; //将按钮的是否可点击与2个输入框是否符合条件进行绑定 RAC(button,enabled)=self.bothTwoTextFieldSignal; //条件的扩展,改变颜色 [self.bothTwoTextFieldSignal subscribeNext:^(NSNumber *x) { if (x.boolValue) { [button setTitleColor:[UIColor greenColor] forState:UIControlStateNormal]; [button setTitle:@"恭喜你,你可以点我" forState:UIControlStateNormal];
}else { [button setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; [button setTitle:@"哎,你不符合,不可以点我" forState:UIControlStateNormal];
} }]; //将按钮的按下时进行绑定 [[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { //将按钮执行的方法放置这里面 UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"恭喜你" message:@"看来你是真心爱刘力,想成为神仙么?" delegate:self cancelButtonTitle:@"想" otherButtonTitles:@"不想",nil]; [alertView show]; [alertView.rac_buttonClickedSignal subscribeNext:^(NSNumber *x) { if ([x isEqualToNumber:[NSNumber numberWithInteger:0]]) { //这里点了第0个按钮 UIAlertView * alertViewOne = [[UIAlertView alloc]initWithTitle:@"你已经成为神仙了" message:@"你回家看看你床底下,多了500万,赏给你的" delegate:self cancelButtonTitle:@"哎呀,我回家了" otherButtonTitles:nil,nil]; [alertViewOne show]; }else { //这里点了第一个按钮 UIAlertView * alertViewTwo = [[UIAlertView alloc]initWithTitle:@"你不想当神仙?!!" message:@"你看看你的皮夹,没钱了吧!我变没了!" delegate:self cancelButtonTitle:@"我知道错了" otherButtonTitles:nil,nil]; [alertViewTwo show]; } }]; }]; } // Configure the cell... return cell; } -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 100; } #pragma mark- 创建输入框 -(UITextField*)creatTextFieldWithView:(UIView*)view WithPlaceHolderStr:(NSString*)placeHolderStr { UITextField * field = [[UITextField alloc]initWithFrame:view.bounds]; field.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth; field.leftViewMode=UITextFieldViewModeAlways; field.autocapitalizationType = UITextAutocapitalizationTypeNone; field.autocorrectionType = UITextAutocorrectionTypeNo; field.clearButtonMode = UITextFieldViewModeWhileEditing; field.placeholder = placeHolderStr; field.font = [UIFont systemFontOfSize:12]; [view addSubview:field]; return field; } #pragma mark- 创建imageview -(UIImageView*)creatImageViewWithView:(UIView*)view { UIImageView * imageView = [[UIImageView alloc]initWithFrame:CGRectMake(CGRectGetMinX(view.bounds),CGRectGetMinY(view.bounds),CGRectGetHeight(view.bounds),CGRectGetHeight(view.bounds))]; return imageView; }
就针对这个例子来讲,我们用ReactiveCocoa会使代码简洁,可读性高。比如,textfield无需在代理里判断,button的响应方法直接用block来替代,alert的按下也无需在代理里面判断。最重要的是,信号源一旦变化,其对应的触发将同步变化。这是一个简单的Demo,后续会逐步研究比较高难度的,比如替换KVO、比如网络下载、比如替换NSOperation等等... 作为一个程序员,除了敲代码,也要密切跟踪最新技术走向,我觉得对我们是有好处的。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |