zoukankan      html  css  js  c++  java
  • 函数响应式编程及ReactiveObjC学习笔记 (三)

    之前讲了RAC如何帮我们实现KVO / 代理 / 事件 / 通知

    今天先不去分析它的核心代码, 我们先看看ReactiveObjC库里面一些特别的东西,  如果大家点开ReactiveObjC目录应该会看到很多category, 今天我们先来看看这些

    我们先从UITextView+RACSignalSupport.h开始看

    #import <UIKit/UIKit.h>
    
    @class RACDelegateProxy;
    @class RACSignal<__covariant ValueType>;
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface UITextView (RACSignalSupport)
    
    @property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy;
    
    - (RACSignal<NSString *> *)rac_textSignal;
    
    @end

    这里有一个属性跟一个方法,  

    关于RACDelegateProxy这个类的用途大概是把初始化传入的代理绑定或者添加给当前正在处理的信号

    给大家一个例子:

    #import "ViewController.h"
    #import <ReactiveObjC.h>
    #import <objc/runtime.h>
    
    @interface ViewController ()<UITextViewDelegate>
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {;
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        // 以UITextViewDelegate来初始化一个RACDelegateProxy
        RACDelegateProxy *delegateProxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextViewDelegate)];
        
        // 注册要实现的方法
        [[delegateProxy
            rac_signalForSelector:@selector(textViewDidBeginEditing:)]
            subscribeNext:^(RACTuple * _Nullable x) {
              
                NSLog(@"开始编辑");
            }];
        
        UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    textView.center
    = self.view.center; textView.backgroundColor = [UIColor greenColor]; // 设置代理为我们创建的RACDelegateProxy, 注意要转义不然会有警告 textView.delegate = (id<UITextViewDelegate>)delegateProxy; [self.view addSubview:textView]; // retain我们创建的delegateProxy, 避免被释放 objc_setAssociatedObject(textView, _cmd, delegateProxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end

    这个一般是RAC内部使用, 我们比较少用. 另外也只能处理没有返回值的代理方法

    可以到UITextview+RACSignalSupport.m里面看看, 也是类似这样用的

    - (RACDelegateProxy *)rac_delegateProxy {
        RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd);
        if (proxy == nil) {
            proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextViewDelegate)];
            objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
    
        return proxy;
    }

    好了, 现在我们来使用下这个UITextView类别唯一的方法

    - (RACSignal<NSString *> *)rac_textSignal;

    大家可以看到, 这个方法会返回一个信号 我们可以对他订阅, 试试看

    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        textView.center          = self.view.center;
        textView.backgroundColor = [UIColor greenColor];
    
        [self.view addSubview:textView];
    
        [[textView
         rac_textSignal]
         subscribeNext:^(NSString * _Nullable x) {
             
             NSLog(@"%@", x);
         }];

    运行看看, 当我们在textView中输入文字的时候会打印:

    2017-07-23 22:41:42.841 RAC[70053:14036438] 1
    2017-07-23 22:41:43.353 RAC[70053:14036438] 11
    2017-07-23 22:41:44.031 RAC[70053:14036438] 111

    所以这个x就是Textview的内容了.

    下面我们看看

    UITextField+RACSignalSupport.h

    #import <UIKit/UIKit.h>
    
    @class RACChannelTerminal<ValueType>;
    @class RACSignal<__covariant ValueType>;
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface UITextField (RACSignalSupport)
    
    - (RACSignal<NSString *> *)rac_textSignal;
    
    - (RACChannelTerminal<NSString *> *)rac_newTextChannel;
    
    @end
    
    NS_ASSUME_NONNULL_END

    这里有两个方法, 我们先看第一个rac_textSignal

        UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 35)];
        
        textField.center          = self.view.center;
        textField.backgroundColor = [UIColor yellowColor];
        
        [[textField
            rac_textSignal]
            subscribeNext:^(NSString * _Nullable x) {
                
                NSLog(@"%@", x);
            }];
        
        [self.view addSubview:textField];

    运行看看, 我们输入数字的时候会打印内容:

    2017-07-23 22:55:45.686 RAC[70205:14118946] 1
    2017-07-23 22:55:46.139 RAC[70205:14118946] 11
    2017-07-23 22:55:46.798 RAC[70205:14118946] 111

    然后我们看看另外一个方法

    - (RACChannelTerminal<NSString *> *)rac_newTextChannel;

    这里涉及到了一个类RACChannelTerminal, 我们点进去看看这个类

    @interface RACChannelTerminal<ValueType> : RACSignal<ValueType> <RACSubscriber>
    
    - (instancetype)init __attribute__((unavailable("Instantiate a RACChannel instead")));
    
    // Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
    - (void)sendNext:(nullable ValueType)value;
    
    @end

    可以看到它是一个RACSignal的子类,  我们先调用看看这个方法

    [[textField
            rac_newTextChannel]
            subscribeNext:^(NSString * _Nullable x) {
                
                NSLog(@"%@", x);
            }];

    效果跟rac_textSignal一样, 那么它有什么特别的用法呢

    它的作用是做双向绑定 关于什么是双向绑定呢? 给大家一个简单的例子:

        UITextField *textFieldA = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 35)];
        
        textFieldA.center          = self.view.center;
        textFieldA.backgroundColor = [UIColor yellowColor];
        
        [self.view addSubview:textFieldA];
        
        UITextField *textFieldB = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 35)];
        
        textFieldB.center          = CGPointMake(self.view.center.x, self.view.center.y + 50);
        textFieldB.backgroundColor = [UIColor yellowColor];
        
        [self.view addSubview:textFieldB];
        
        RACChannelTerminal *terminalA = [textFieldA rac_newTextChannel];
        RACChannelTerminal *terminalB = [textFieldB rac_newTextChannel];
        
    
        [terminalA subscribe:terminalB];
        [terminalB subscribe:terminalA];

    运行可以看到, 改变textFieldA的值textFieldB的值也会跟着改变, 反过来也一样.

    这里如果要实现双向绑定, 其实还有一个简单的方法:

    RACChannelTo(textFieldA, text) = RACChannelTo(textFieldB, text);

    大家可以试试看.

    如果我们不仅仅想让两个绑定对象之间的值简单的相等而已呢? 比如textFieldA的值是123的时候textFieldB的值要为321要怎么处理呢?

    这里我们先说一个一会用到的方法: map

    map方法,将会创建一个和原来一模一样的信号,只不过新的信号传递的值变为了block(value)。

        [[[textField
            rac_textSignal]
            map:^id _Nullable(NSString * _Nullable value) {
                
                if ([value isEqualToString:@"11"]) {
                    
                    return @"1";
                } else {
                    
                    return @"0";
                }
            }] subscribeNext:^(id  _Nullable x) {
                
                NSLog(@"%@", x);
            }];

    运行看看, 当我们输入1, 会打印0, 输入11的时候会打印1, 这里就是把传递的值从textField的text转变成为我们的1 和 0;

    然后有个特别的地方, 加入我们知道传递的值的类型, 我们就可以直接把后面订阅的block里面的参数类型直接改成我们知道的类型

    例如把id改为NSString *运行结果也是一样的, 这个是RAC一个比较特别的地方

    那么要实现上面的123 到 321可以这样写:

        RACChannelTerminal *terminalA = [textFieldA rac_newTextChannel];
        RACChannelTerminal *terminalB = [textFieldB rac_newTextChannel];
        
        [[terminalA
            map:^id _Nullable(id  _Nullable value) {
                
                if ([value isEqualToString:@"123"]) {
                    
                    return @"321";
                }
                
                return value;
            }]
            subscribe:terminalB];
    
        [terminalB subscribe:terminalA];

    大家可以自己运行看看效果, 当textFieldA输入123的时候textFieldB会变为321

    下面我们看看

    UIActionSheet+RACSignalSupport.h

    #import <UIKit/UIKit.h>
    
    @class RACDelegateProxy;
    @class RACSignal<__covariant ValueType>;
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface UIActionSheet (RACSignalSupport)
    
    @property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy;
    
    - (RACSignal<NSNumber *> *)rac_buttonClickedSignal;
    
    @end

    rac_delegateProxy跟之前textview是一样的用法这里开始就不再解释这类属性了

    我们直接试着使用rac_buttonClickedSignal

        UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"RAC ActionSheet" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"OK" otherButtonTitles:@"Other", nil];
        
        [[actionSheet
            rac_buttonClickedSignal]
         subscribeNext:^(NSNumber * _Nullable x) {
             
             NSLog(@"%@", x);
         }];
        
        [actionSheet showInView:self.view];

    运行看看, x是actionSheet上按钮的编号, 我们拿到编号就可以做响应的事件处理了.

    UIAlertView+RACSignalSupport.h

    @interface UIAlertView (RACSignalSupport)
    
    @property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy;
    
    
    - (RACSignal<NSNumber *> *)rac_buttonClickedSignal;
    
    
    - (RACSignal<NSNumber *> *)rac_willDismissSignal;
    
    @end
    
    NS_ASSUME_NONNULL_END

    它有两个方法, 一个是点击的时候用, 一个是dismiss的时候用

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"RAC" message:@"RAC Alert" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
        
        [[alert
            rac_buttonClickedSignal]
            subscribeNext:^(NSNumber * _Nullable x) {
                
                NSLog(@"click: x");
            }];
        
        [[alert
            rac_willDismissSignal]
            subscribeNext:^(NSNumber * _Nullable x) {
                
                NSLog(@"dismiss: %@", x);
            }];
        
        [alert show];

    UIControl+RACSignalSupport.h

    #import <UIKit/UIKit.h>
    
    @class RACSignal<__covariant ValueType>;
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface UIControl (RACSignalSupport)
    
    - (RACSignal<__kindof UIControl *> *)rac_signalForControlEvents:(UIControlEvents)controlEvents;
    
    @end
    
    NS_ASSUME_NONNULL_END

    只有一个方法, 这个之前讲过是做UIControllerEvent处理的, 再给个例子:

        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        
        [button setFrame:CGRectMake(0, 0, 100, 35)];
        [button setCenter:self.view.center];
        [button setBackgroundColor:[UIColor yellowColor]];
        [button setTitle:@"按钮" forState:UIControlStateNormal];
        
        [[button
            rac_signalForControlEvents:UIControlEventTouchUpInside]
            subscribeNext:^(__kindof UIControl * _Nullable x) {
                
                NSLog(@"点击了按钮");
                x.backgroundColor = [UIColor redColor];
            }];
        
        [self.view addSubview:button];

    UIDatePicker+RACSignalSupport.h

    #import <UIKit/UIKit.h>
    
    @class RACChannelTerminal<ValueType>;
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface UIDatePicker (RACSignalSupport)
    
    - (RACChannelTerminal<NSDate *> *)rac_newDateChannelWithNilValue:(nullable NSDate *)nilValue;
    
    @end
    
    NS_ASSUME_NONNULL_END

    它只有一个绑定的方法, 直接给大家一个例子:

    大概效果为我们在Controller中添加一个UITextField跟一个UIDatePicker, 然后获取他们的RACChannelTerminal,

    将UIDatePicker绑定给UITextField, 当我们滚动datePicker的时候 textField的值会跟着改变

       UIDatePicker *datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 160)];
        datePicker.center = self.view.center;
        datePicker.backgroundColor = [UIColor redColor];
        
        [self.view addSubview:datePicker];
        
        UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 180, 35)];
        textField.center = CGPointMake(self.view.center.x, self.view.center.y - 100);
        textField.backgroundColor = [UIColor yellowColor];
        
        [self.view addSubview:textField];
        
        
        RACChannelTerminal *datePickerTerminal = [datePicker rac_newDateChannelWithNilValue:[NSDate date]];
        RACChannelTerminal *textFieldTerminal  = [textField rac_newTextChannel];
        
        [[datePickerTerminal
            map:^id _Nullable(id  _Nullable value) {
                
                NSLog(@"%@", value);
                NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
                [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
                return [dateFormatter stringFromDate:value];
            }]
            subscribe:textFieldTerminal];

    运行截图:

    这篇先写到这里, 下次我们再接着看

  • 相关阅读:
    从实验室走向世界:HSP90抑制剂,一种新的癌症药物
    RabbitMQ整合Spring Booot【消费者应答模式】
    RabbitMQ整合Spring Booot【点对点模式】
    Mac删除自带的abc输入法
    Docker搭建Kafka
    IK分词器
    Docker搭建ES集群
    Logstash配置同步mysql到es配置
    ELK分布式日志收集搭建和使用
    Docker安装ES和Kibana
  • 原文地址:https://www.cnblogs.com/zhouxihi/p/7226917.html
Copyright © 2011-2022 走看看