GICDataBinding是一款基于NSProxy
开发的数据绑定库,支持数据绑定
和事件绑定
(以为好的各位,不要吝啬您的star
)有以下特点功能:javascript
支持JS表达式
前端
@"'姓名:'+$.name.split('').reverse().join('') + ',性别:'+($.isMale?'男':'女')"
复制代码
- 表达式仅支持单一表达式,若是表达式字符串中出现多个表达式,可能会出现意外。
- 你能够在单一表达式中调用任意JS中的方法,甚至调用你
预先注入
的方法。这样一来就为基于数据绑定开发的功能增长了无限可能。你能够直接将一些之前在ViewModel
中定义的方法直接注入到JSCore中,使得能够直接在JS表达中调用这些方法- 你能够直接在JS表达式中对数据源中的属性作计算,而后将结果返回。
灵活基于JSCore
开发、注入各类方法
、Class
将会使得开发某些功能变得异常的简单。甚至若是你的部分UI是基于Texture
这样的支持自动布局的库开发的话,那么对于构建UI这样的任务变得异常的简单。vue
单向绑定java
双向绑定git
双向绑定在本质上仍是基于单向绑定的,可是GICDataBinding对某些UI组件进行了封装,使得能够直接一行代码就能完成双向绑定。相似于前端
vue
中的model
github
支持对NSMutableArray
进行观察。objective-c
当数组内容变动后,你能够获得相应的回调。这样一来你能够开发出类前端
VUE
那样的自动根据数组内容变动,从而update UI的功能。express
支持事件绑定
。重点数组
当前对于
UIControl
已经实现了相关的事件绑定。其余的事件开发者能够本身去实现。实现的方法也是很简单的安全这里面的技术基础就是,基于NSProxy实现对方法调用的拦截,从而能够实现相似方法交换的目的。也就是说这里能够不经过方法交换技术就能实现相似的功能需求。PS:有兴趣能够看下这部分源码,我相信必定会让你有所收获。
固然也支持Swift
开发。可是要求Swift
中的数据类必须是NSObject
子类。
pod 'GICDataBinding'
复制代码
github地址:github.com/ghwghw4/GIC…
全部的数据绑定功能的前提是一个能够被观察
的对象,就像KVO
那样的。可是GICDataBinding
不是基于KVO
开发的,而是基于NSProxy
开发的,所以在进行数据绑定之前,须要对你的数据源作一个转换,将数据源变成可观察对象,这一切的原理基础是基于NSProxy
实现的。
首先你要有一个数据类,好比UserInfo
这样的数据模型
数据类必须实现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
复制代码
对数据模型调用gic_observer
方法,你会得到一个能够被观察的数据模型。
TestData *data = [[TestData testData] gic_observer];
复制代码
JS表达式中的
$
表示数据源自己,所以若是你想要访问数据源的某个属性或方法,那么必须使用$
来访问
普通的单向绑定。
[btn gic_addBinding:createDataBinding(theme, @"$.backgroundColor", ^(UIButton *target, id newValue) {
[target setTitleColor:newValue forState:UIControlStateNormal];
})];
复制代码
这样当数据源中的backgroundColor
属性改变的时候就会触发回调
直接将表达式绑定到目标对象的属性上。
[lbl gic_addBinding:createPropertyDataBinding(user, @"'计数:'+$.timeTick", @"text")];
复制代码
经过createPropertyDataBinding
能够建立一个属性绑定,将表达式'计数:'+$.timeTick
的结果自动绑定到UILable
的 text
属性。
注意:createPropertyDataBinding 在内部实现的时候基于
setValue:forKey
来实现的,所以确保这个属性是支持KVC的。若是这个属性不支持,那么使用第一种方法也能达到数据绑定目的。
双向绑定。
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
改变的时候,UISwitch
的isOn
也会跟着改变。而当UISwitch
的isOn
被改变的时候,数据源中的isMale
属性也会跟着改变。但这里你不用担忧会陷入死循环,类库已经作了比较处理,若是两次变动的value是同样的,那么不会重复触发。
对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
支持的课观察方法的枚举都已经在上面列出。
在表达式中调用注入的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方法有两种方式
直接以下上面的方法,采用block方式注入
调用 [[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
复制代码
初始化ViewModel
testViewModel = [[[TestViewModel alloc] init] gic_observer];
复制代码
绑定按钮的点击事件。
[btn gic_addEventBinding:[GICEventBinding bindingWidthDataSource:testViewModel expression:@"$.onButtonClicked(`${$.v1}----${$.v2}`)"] forControlEvents:UIControlEventTouchUpInside];
复制代码
从这里你能够看到,当按钮点击事件触发后,就会执行JS脚本
$.onButtonClicked(`${$.v1}----${$.v2}`)
复制代码
从这里看到,是调用的
testViewModel
中的onButtonClicked
方法,而且传入字符串参数。
绑定事件中直接设置数据源的value
[btn gic_addEventBinding:[GICEventBinding bindingWidthDataSource:testViewModel expression:@"$.v2++"] forControlEvents:UIControlEventTouchDown];
复制代码
这里每当点击事件触发后,就会对数据源的
v2
属性+1
。若是有其余地方绑定了v2
属性,那么也会同时更新数据绑定
事件绑定直接调用本地代码。
[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
数据绑定系统,你能够作一些不少之前实现起来比较复杂的功能。好比:
app 主题(Example 有例子)
能够直接基于绑定系统,将一些主题元素绑定到提供主题数据的模型。这样当用户修改主题的时候,app能够作出实时的改变。
从新思考ViewModel
的定义。将ViewModel
JS化
你如今能够把部分或者整个原来已有的ViewModel移入JSCore,而后经过数据绑定系统直接调用
配合Texture
实现整个UI 基于绑定系统的可响应式设计。
HotFix
因为数据绑定和事件绑定都是采用JS表达式的方式调用,所以理论是上能够直接经过下发JS脚原本动态修复、增长功能的。固然,这仅仅是理论上,真要实现起来仍是有很大开发量的