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腳原本動態修復、增長功能的。固然,這僅僅是理論上,真要實現起來仍是有很大開發量的

相關文章
相關標籤/搜索