一、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;
}
4、总结。
就针对这个例子来讲,我们用ReactiveCocoa会使代码简洁,可读性高。比如,textfield无需在代理里判断,button的响应方法直接用block来替代,alert的按下也无需在代理里面判断。最重要的是,信号源一旦变化,其对应的触发将同步变化。这是一个简单的Demo,后续会逐步研究比较高难度的,比如替换KVO、比如网络下载、比如替换NSOperation等等...
作为一个程序员,除了敲代码,也要密切跟踪最新技术走向,我觉得对我们是有好处的。
原文链接:https://www.f2er.com/react/308178.html