ReactiveCocoa代码实践之-UI组件的RAC信号操作

所属分类: 软件编程 / Android 阅读数: 984
收藏 0 赞 0 分享

相关阅读:

ReactiveCocoa代码实践之-更多思考

ReactiveCocoa代码实践之-RAC网络请求重构这一节是自己对网络层的一些重构,本节是自己一些代码小实践做出的一些demo程序,基本涵盖大多数UI控件操作。

一.用UISlider实现调色板

假设我们现在做一个demo,上面有一个View用来展示颜色,下面有三个UISlider滑竿分别控制RGB的色值,随着不同滑竿的拖动上面view的颜色会随之改变。 可以先脑补一下不用RAC该怎么写。 如果使用RAC只需要将三个信号包装起来用适当的操作就能实现。

// 拖线的UI控件
@property (weak, nonatomic) IBOutlet UIView *topView;
@property (weak, nonatomic) IBOutlet UISlider *slider;
@property (weak, nonatomic) IBOutlet UISlider *slider;
@property (weak, nonatomic) IBOutlet UISlider *slider;
// viewDidLoad中
// 分别将三个控件的改变都包成一个信号。
RACSignal *s = [[self.slider rac_newValueChannelWithNilValue:@]startWith:@];
RACSignal *s = [[self.slider rac_newValueChannelWithNilValue:@]startWith:@];
RACSignal *s = [[self.slider rac_newValueChannelWithNilValue:@]startWith:@];
RACSignal *threeSignal = [RACSignal combineLatest:@[s,s,s] reduce:^id(NSNumber* value,NSNumber* value,NSNumber* value){
return @[value,value,value];
}];
// 监听这个"合成"后的信号,改变view的颜色
[threeSignal subscribeNext:^(NSArray *arr) {
self.topView.backgroundColor = [UIColor colorWithRed:[arr[] doubleValue] green:[arr[] doubleValue] blue:[arr[] doubleValue] alpha:];
}]; 

上面的startWith:@0需要注意,如果不加这个初始值那必须在三个滑竿都动一下才能显示颜色。 上面使用的方法时UISlider专属的,也可以用下面的方法写,这个是UIControl的方法会支持更多其他UI控件。

RACSignal *s1 = [[[self.slider1 rac_signalForControlEvents:UIControlEventValueChanged] map:^id(id value) {
return @(self.slider1.value);
}] startWith:@0];

二.简洁代码实现登录逻辑

在UI控件中难点不多,但是值得注意的就是各种状态的多级管理,如果哪里疏忽了就很容易造成bug,这也就导致很多地方有判断结构,并且各种来回赋值。 假设现在需要做一个登录框,有账号密码和同意条款三项,必须满足账号密码大于2位且选择了同意,才允许注册。 旧的写法非常麻烦,还需要监听valueChange事件等。如果用RAC只需要写如下代码:

@property (weak, nonatomic) IBOutlet UITextField *accountTxt;
@property (weak, nonatomic) IBOutlet UITextField *pwdTxt;
@property (weak, nonatomic) IBOutlet SXSwitch *agreeSw; // 同意条款
@property (weak, nonatomic) IBOutlet UIButton *loginBtn; // 注册按钮
// viewDidLoad方法
self.loginBtn.enabled = NO;
RAC(self.loginBtn , enabled) = [RACSignal combineLatest:@[self.accountTxt.rac_textSignal , self.pwdTxt.rac_textSignal, self.agreeSw.rac_newOnChannel] reduce:^(NSString *account, NSString *pwd, NSNumber *isOn){
return @((account.length > )&&(pwd.length >)&&[isOn boolValue]);
}]; 

这其中combineLatest数组中用的都是控件专属的信号, 也可以使用RAC(self.agreeSw, on) 这种写法直接把某一个属性的状态用信号传过来。但是这里需要注意:假设你监听了A类的B属性时,只有走了B属性的set方法才会被监听捕获,如果是通过其他方法修改的属性值则无效。 比如UISwitch的来回拨动过程中并没有走on这个属性的set方法。

三.通过interval方法实现时钟

这是一种默认循环的方法,除非你通过控制Disposable把他禁了。 interval这个方法就是传入一个参数是间隔时间,然后内部每隔这一段时间就发一个[NSDate date]的对象,然后block内部把这个date设置一个格式以字符串的方法返回。

RAC(self, timeLabel.text) = [[[RACSignal interval: onScheduler:[RACScheduler currentScheduler]] startWith:[NSDate date]] map:^id (NSDate *value) {
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:value];
return [NSString stringWithFormat:@"%ld:%ld:%ld", (long)dateComponents.hour, (long)dateComponents.minute, (long)dateComponents.second];
}];

四.其他控件事件操作

除了上面的UIButton,UISlider,UIControl的分类方法还有很多操作

UISegmentedControl (RACSignalSupport)分类就为此控件提供了便捷处理方法,相比于常规的监听seg的元素点击事件,再取出当前选中的index。RACSignal可以直接得到需要的值

[[self.seg rac_newSelectedSegmentIndexChannelWithNilValue:@]subscribeNext:^(id x) {
// 返回的基本数据类型都被装包成NSNumber,可在此做一些判断操作
NSLog(@"selectIndex-%@",x);
}];

UIDatePicker (RACSignalSupport)分类为时间选择框封装了一个操作,每当选框改变时返回NSDate类型

[[picker rac_newDateChannelWithNilValue:[NSDate date]]subscribeNext:^(NSDate *x) {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"HH:mm";
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"];
NSString *dateStr = [dateFormatter stringFromDate:x];
}]; 

如果在这里给控件赋值,每一次改动都会让展示控件的值更新,如果有的设计不希望这么频繁只有在点击确认后再将时间显示可以根据自己喜好自行赋值。

除此这些还有很多UI控件绑定的方法 UIAlertView (RACSignalSupport) 里面就提供了一些方法比如点击弹窗按钮可以在subscribeNext里统一处理各个按钮的点击事件。 但是现在UIAlertView已被UIAlertController取代所以,UIAlertView和UIActionSheet这里可以忽略不提。

五.生命周期相关操作

UITableView和UICollectionView的Cell都有重用的机制,如果给这个Cell绑定了一些监听,那这个Cell被重用它子控件的监听该何去何从?UITableViewCell (RACSignalSupport)、UICollectionReusableView (RACSignalSupport)这两个分类里提供了即将重用时的信号rac_prepareForReuseSignal

做过两个类似的场景,一个是tableView的cell回复按钮点击会跳到回复页,一个是collection的item内有个按钮点击就变颜色。

// UITableViewDataSource
- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
SXFeedbackCell * cell = [tableView dequeueReusableCellWithIdentifier:@"SXFeedbackCell"];
@weakify(self)
[[[cell.replyButton rac_signalForControlEvents:UIControlEventTouchUpInside] takeUntil:cell.rac_prepareForReuseSignal]
subscribeNext:^(UIButton *x) {
@strongify(self)
// 处理一些其他逻辑
[self.navigationController pushViewController:[SXReplyPage new] animated:YES];
}];
return cell;
}
// UICollectionViewDataSource
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell * cell = [collectionView dequeueReusableCellWithIdentifier:@"SXDownloadCell"];
[[[cell.changeBtn rac_signalForControlEvents:UIControlEventTouchUpInside] takeUntil:cell.rac_prepareForReuseSignal]
subscribeNext:^(UIButton *x) {
cell.backgroundColor = [UIColor grayColor];
}];
return cell;
}

其中takeUntil操作是监听某个事件直到什么时候结束。当这个cell即将重用时rac_prepareForReuseSignal到来会触发disposable信号结束监听。

非重用类型的控件的生命周期可以用rac_willDeallocSignal 信号监听,但是在开发中很少会用到此信号,因为大多是信号操作的内部代码里都帮你做了这个操作,即监听一个事件直到自己结束时停止监听。

// rac_textSignal源码
- (RACSignal *)rac_textSignal {
@weakify(self);
return [[[[[RACSignal
defer:^{
@strongify(self);
return [RACSignal return:self];
}]
concat:[self rac_signalForControlEvents:UIControlEventAllEditingEvents]]
map:^(UITextField *x) {
return x.text;
}]
takeUntil:self.rac_willDeallocSignal]
setNameWithFormat:@"%@ -rac_textSignal", self.rac_description];
}
// rac_signalForControlEvents源码
- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {
@weakify(self);
return [[RACSignal
createSignal:^(id<RACSubscriber> subscriber) {
@strongify(self);
[self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
[self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
[subscriber sendCompleted];
}]];
return [RACDisposable disposableWithBlock:^{
@strongify(self);
[self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
}];
}]
setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", self.rac_description, (unsigned long)controlEvents];
}

关于ReactiveCocoa代码实践之-UI组件的RAC信号操作就给大家介绍这么多,希望对大家有所帮助!

更多精彩内容其他人还在看

老生常谈Android HapticFeedback(震动反馈)

下面小编就为大家带来一篇老生常谈Android HapticFeedback(震动反馈)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

详谈OnTouchListener与OnGestureListener的区别

下面小编就为大家带来一篇详谈OnTouchListener与OnGestureListener的区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Android仿知乎悬浮功能按钮FloatingActionButton效果

前段时间在看属性动画,恰巧这个按钮的效果可以用属性动画实现,下面通过本文给大家分享adroid仿知乎悬浮功能按钮FloatingActionButton效果,需要的朋友参考下吧
收藏 0 赞 0 分享

解决Android V7后自定义Toolbar、ActionBar左侧有空白问题

这篇文章主要介绍的Android V7后自定义Toolbar、ActionBar左侧有空白问题的解决方法,需要的朋友可以参考下
收藏 0 赞 0 分享

Android常见控件使用详解

这篇文章主要为大家详细介绍了Android常见控件的使用方法,包括ProgressBar进度条控件、AlertDialog对话框控件等,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Android实现简洁的APP更新dialog数字进度条

这篇文章主要为大家详细介绍了Android实现简洁的APP更新dialog数字进度条,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Android 判断当前语言环境是否是中文环境

本文主要介绍了Android 判断当前语言环境是否是中文环境的方法。具有很好的参考价值。下面跟着小编一起来看下吧
收藏 0 赞 0 分享

详谈Android中Matrix的set、pre、post的区别

下面小编就为大家带来一篇详谈Android中Matrix的set、pre、post的区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Android实现登录界面记住密码的存储

这篇文章主要为大家详细介绍了Android SharedPreferrences实现登录界面记住密码的存储,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Android 使用SharedPreferrences储存密码登录界面记住密码功能

Android存储方式有很多种,在这里所用的存储方式是SharedPreferrences, 其采用了Map数据结构来存储数据,以键值的方式存储,可以简单的读取与写入,下面通过实例代码给大家讲解下,需要的朋友参考下吧
收藏 0 赞 0 分享
查看更多