iOS--GICDataBinding | 基于NSProxy开发的数据绑定库--支持JS表达式

简介

GICDataBinding是一款基于NSProxy开发的数据绑定库,支持数据绑定事件绑定(以为好的各位,不要吝啬您的star)有以下特点功能:javascript

  1. 支持JS表达式前端

    @"'姓名:'+$.name.split('').reverse().join('') + ',性别:'+($.isMale?'男':'女')"
    复制代码
    1. 表达式仅支持单一表达式,若是表达式字符串中出现多个表达式,可能会出现意外。
    2. 你能够在单一表达式中调用任意JS中的方法,甚至调用你预先注入的方法。这样一来就为基于数据绑定开发的功能增长了无限可能。你能够直接将一些之前在ViewModel中定义的方法直接注入到JSCore中,使得能够直接在JS表达中调用这些方法
    3. 你能够直接在JS表达式中对数据源中的属性作计算,而后将结果返回。

    灵活基于JSCore开发、注入各类方法Class将会使得开发某些功能变得异常的简单。甚至若是你的部分UI是基于Texture这样的支持自动布局的库开发的话,那么对于构建UI这样的任务变得异常的简单。vue

  2. 单向绑定java

  3. 双向绑定git

    双向绑定在本质上仍是基于单向绑定的,可是GICDataBinding对某些UI组件进行了封装,使得能够直接一行代码就能完成双向绑定。相似于前端vue中的modelgithub

  4. 支持对NSMutableArray进行观察。objective-c

    当数组内容变动后,你能够获得相应的回调。这样一来你能够开发出类前端VUE那样的自动根据数组内容变动,从而update UI的功能。express

  5. 支持事件绑定重点数组

    当前对于UIControl已经实现了相关的事件绑定。其余的事件开发者能够本身去实现。实现的方法也是很简单的安全

    这里面的技术基础就是,基于NSProxy实现对方法调用的拦截,从而能够实现相似方法交换的目的。也就是说这里能够不经过方法交换技术就能实现相似的功能需求。PS:有兴趣能够看下这部分源码,我相信必定会让你有所收获。

  6. 固然也支持Swift开发。可是要求Swift中的数据类必须是NSObject子类。

安装

pod 'GICDataBinding'
复制代码

github地址:github.com/ghwghw4/GIC…

使用方法

数据模型

全部的数据绑定功能的前提是一个能够被观察的对象,就像KVO那样的。可是GICDataBinding不是基于KVO开发的,而是基于NSProxy开发的,所以在进行数据绑定之前,须要对你的数据源作一个转换,将数据源变成可观察对象,这一切的原理基础是基于NSProxy实现的。

  1. 首先你要有一个数据类,好比UserInfo这样的数据模型

  2. 数据类必须实现GICObserverProtocol协议,这个协议实际上是一个空协议,仅仅是用来标记该类是可观察的。因为GICDataBinding支持嵌套的,所以全部数据模型中的对象想要能够被观察,那么都须要实现GICObserverProtocol协议。

    @interface TestData : NSObject<GICObserverProtocol>
    @property(nonatomic,copy)NSString *name;
    @property(nonatomic,assign)NSInteger age;
    @property(nonatomic,assign)BOOL isMale;
    @property(nonatomic,strong)UserAddress *address;
    @property (nonatomic,strong)NSDictionary *dict;
    @property (nonatomic,strong)NSMutableDictionary *dict2;
    @property (nonatomic,strong)NSArray *array;
    @property (nonatomic,strong)NSMutableArray *mutArray;
    @property(nonatomic,assign)NSInteger timeTick;
    +(instancetype)testData;
    @end
    复制代码
  3. 对数据模型调用gic_observer方法,你会得到一个能够被观察的数据模型。

    TestData *data = [[TestData testData] gic_observer];
    复制代码

数据绑定

JS表达式中的$表示数据源自己,所以若是你想要访问数据源的某个属性或方法,那么必须使用$来访问

  1. 普通的单向绑定。

    [btn gic_addBinding:createDataBinding(theme, @"$.backgroundColor", ^(UIButton *target, id newValue) {
       [target setTitleColor:newValue forState:UIControlStateNormal];
    })];
    复制代码

    这样当数据源中的backgroundColor属性改变的时候就会触发回调

  2. 直接将表达式绑定到目标对象的属性上。

    [lbl gic_addBinding:createPropertyDataBinding(user, @"'计数:'+$.timeTick", @"text")];
    复制代码

    经过createPropertyDataBinding能够建立一个属性绑定,将表达式'计数:'+$.timeTick的结果自动绑定到UILabletext属性。

    注意:createPropertyDataBinding 在内部实现的时候基于setValue:forKey来实现的,所以确保这个属性是支持KVC的。

    若是这个属性不支持,那么使用第一种方法也能达到数据绑定目的。

  3. 双向绑定。

    UISwitch *sw = [[UISwitch alloc] initWithFrame:CGRectMake(10, CGRectGetMaxY(btn.frame)+10, 100, 44)];
    [sw gic_towwayBinding:user propertyName:@"isMale"];
    [self.view addSubview:sw];
    复制代码

    你能够经过gic_towwayBinding来建立一个双向绑定。propertyName表示的是数据源中的属性名称。也就说,当数据源中的isMale改变的时候,UISwitchisOn也会跟着改变。而当UISwitchisOn被改变的时候,数据源中的isMale属性也会跟着改变。但这里你不用担忧会陷入死循环,类库已经作了比较处理,若是两次变动的value是同样的,那么不会重复触发。

  4. NSMutableArray进行观察。

    第一步固然是须要将array转换成可观察对象了。经过调用gic_observer来实现。。

    arrayObserve.changedBlock = ^(NSMutableArray *mutArray, GICMutableArrayChangedType changedType, NSArray *params) {
                    switch (changedType) {
                        case GICMutableArrayChangedAddObject:
                            NSLog(@"GICMutableArrayChangedAddObject:%@",params[0]);
                            break;
                        case GICMutableArrayChangedRemoveObject:
                            NSLog(@"GICMutableArrayChangedRemoveObject:%@",params[0]);
                            break;
                        case GICMutableArrayChangedRemoveAllObjects:
                            NSLog(@"GICMutableArrayChangedRemoveAllObjects");
                            break;
                        case GICMutableArrayChangedRemoveLastObject:
                            NSLog(@"GICMutableArrayChangedRemoveLastObject:%@",params[0]);
                            break;
                        case GICMutableArrayChangedSortArray:
                            NSLog(@"GICMutableArrayChangedSortArray:%@",mutArray);
                            break;
                        case GICMutableArrayChangedInsertObject:
                            NSLog(@"GICMutableArrayChangedInsertObject:%@,index:%@",params[0],params[1]);
                            break;
                        case GICMutableArrayChangedReplaceObject:
                            NSLog(@"GICMutableArrayChangedReplaceObject:%@,index:%@",params[0],params[1]);
                            break;
                        default:
                            break;
                    }
                };
    复制代码

    目前GICDataBinding支持的课观察方法的枚举都已经在上面列出。

  5. 在表达式中调用注入的JS方法。

    [GICJSContextManager manager].jsContext[@"customFunc"] = ^(JSValue*value){
       //这里以将Color 转换成字符串为例
       UIColor *color = [value toObject];
       CGFloat r,g,b,a;
       [color getRed:&r green:&g blue:&b alpha:&a];
       return [NSString stringWithFormat:@"r:%f,g:%f,b:%f",r,g,b];
    };
    // 数据绑定表达式直接调用js方法
    [lbl gic_addBinding:createPropertyDataBinding(theme, @"'颜色转换:'+customFunc($.titleColor)", @"text")];
    复制代码

    注入JS方法有两种方式

    1. 直接以下上面的方法,采用block方式注入

    2. 调用 [[GICExpresionCalculator calculator].jsContext evaluateScript:jsString] 注入JS脚本

事件绑定

GICDataBinding中的事件绑定只能调用JS代码,可是得益于JSCore可以直接调用本地代码,也就意味着你也能够直接在事件绑定中调用本地代码。

在作事件绑定前,首先你要有一个ViewModel。一样,这个ViewModel须要实现GICObserverProtocol协议。好比下面:

@interface TestViewModel : NSObject<GICObserverProtocol>
@property (nonatomic,copy)NSString *v1;
@property (nonatomic,assign)NSInteger v2;
/// 全部能够被JS调用的方法,都必须带有一个参数,不然没法调用
-(void)onButtonClicked:(id)param;
@end
复制代码
  1. 初始化ViewModel

    testViewModel = [[[TestViewModel alloc] init] gic_observer];
    复制代码
  2. 绑定按钮的点击事件。

    [btn gic_addEventBinding:[GICEventBinding bindingWidthDataSource:testViewModel expression:@"$.onButtonClicked(`${$.v1}----${$.v2}`)"] forControlEvents:UIControlEventTouchUpInside];
    复制代码

    从这里你能够看到,当按钮点击事件触发后,就会执行JS脚本

    $.onButtonClicked(`${$.v1}----${$.v2}`)
    复制代码

    从这里看到,是调用的testViewModel中的onButtonClicked方法,而且传入字符串参数。

  3. 绑定事件中直接设置数据源的value

    [btn gic_addEventBinding:[GICEventBinding bindingWidthDataSource:testViewModel expression:@"$.v2++"] forControlEvents:UIControlEventTouchDown];
    复制代码

    这里每当点击事件触发后,就会对数据源的v2属性+1。若是有其余地方绑定了v2属性,那么也会同时更新数据绑定

  4. 事件绑定直接调用本地代码。

    [btn gic_addEventWatch:^(NSArray * _Nullable params) {
       NSLog(@"点击了");
    } forControlEvents:UIControlEventTouchUpInside];
    复制代码

    这里其实已经不算事件绑定了,能够说是事件代理。GICDataBinding基于NSProxy实现了一套能够直接block回调按钮事件的库。如今你无需RAC就能实现一样的通能。事实上,你能够对任意对象的任意方法进行watch,在某些状况下,你压根就无需方法交换就能实现相似方法交换的功能,并且这种基于NSProxy实现调用拦截的过程是安全、不冲突的,不会出现方法交换可能引发的crash问题(屡次交换)。

注意:

全部能够在事件绑定中被调用的ViewModel中定义的方法,必须带有一个参数,哪怕这个参数你不会用到,并且目前只支持带有一个参数的方法。若是你须要传入多个参数,目前惟一的方法是以数组的方式传入

说明

不论是数据绑定仍是事件绑定,由于都是基于JS表达式来实现的,所以想要熟练的将数据绑定应用到项目中,还须要您对JavaScript有必定的了解,另外GICDataBinding的JS引擎是基于JavaScriptCore来实现的,所以若是你想在项目中扩展JSCore甚至想要之前端开发同样,直接在JS中建立ViewModel,那么须要你对JavaScriptCore有必定的了解

应用

基于GICDataBinding数据绑定系统,你能够作一些不少之前实现起来比较复杂的功能。好比:

  1. app 主题(Example 有例子)

    能够直接基于绑定系统,将一些主题元素绑定到提供主题数据的模型。这样当用户修改主题的时候,app能够作出实时的改变。

  2. 从新思考ViewModel的定义。将ViewModel JS化

    你如今能够把部分或者整个原来已有的ViewModel移入JSCore,而后经过数据绑定系统直接调用

  3. 配合Texture实现整个UI 基于绑定系统的可响应式设计。

  4. HotFix

    因为数据绑定和事件绑定都是采用JS表达式的方式调用,所以理论是上能够直接经过下发JS脚原本动态修复、增长功能的。固然,这仅仅是理论上,真要实现起来仍是有很大开发量的

相关文章
相关标签/搜索