用JSON 實(shí)現(xiàn)iOS UI 實(shí)現(xiàn)原理說明

近日寫了一個好玩的庫JSONRnederKit
大概整個人處于空窗期吧馋贤,閑不下來,同時最近經(jīng)歷了一些事情畏陕,就讓寫代碼來填充自己配乓。
每次因?yàn)樾枨蟾耡pp,時間都非常長惠毁。比如說某個節(jié)日犹芹,想做些彩蛋,你可能就要更新版本了鞠绰。為了解決這個痛點(diǎn)羽莺,突發(fā)奇想,能不能用JSON 做一些簡單的單頁應(yīng)用呢洞豁,事實(shí)上是完全可以的。

截圖如下

效果圖

核心文件

SSJSContext.m,SSBaseRenderController.m,NSObject+SSRende.m,SSKit.js,

文件 作用
SSJSContext.m 負(fù)責(zé)接收J(rèn)SON 生成新的容器View 返回給外界使用
SSBaseRenderController.m 接收SSJSContext 返回的容器視圖并顯示
NSObject+SSRende.m 一共一個方法荒给,調(diào)用OC 對象的任何方法
SSKit.js 仿照UIKit丈挟,實(shí)現(xiàn)JS 的數(shù)據(jù)結(jié)構(gòu)
SSTool.js 提供字符串解析幫助

流程圖

st=>start: 獲取JSON 
e=>end: 顯示結(jié)束
op1=>operation: SSJSContext提供JSON和wrapperView給JS
op2=>operation: JS 接收J(rèn)SON 開始解析
op3=>operation: 解析完畢,JS調(diào)用OC 生成視圖并設(shè)置各種屬性
op4=>operation: 設(shè)置完畢志电,通知jsContext曙咽,并返回wrapperView 
op4=>operation: renderController 接收wrapperView

st->op1->op2->op3->op4->e

如何解析JSON

這里面肯定少不了OC和JS交互的,為了方便交互挑辆,我在SSJSContextJS 定義了oc_invokeWithArgs(ocObj,method,args);這樣JS可以調(diào)用任意OC對象的任意方法例朱,同時定義了一系列和OC 對應(yīng)的類,繼承關(guān)系也對應(yīng)鱼蝉,例如:
NSObject->NSObject,
Controller->UIViewController,
View->UIView
...

//JS調(diào)用該函數(shù)可以達(dá)到調(diào)用OC實(shí)例對象的方法洒嗤,其中JS傳遞的參數(shù)會自動轉(zhuǎn)化為OC相應(yīng)的類型
self[@"oc_invokeWithArgs"] = ^id(JSValue *ocPointer,
                            NSString *methodName,
                            JSValue *args){
   id ocObj           = [ocPointer toObject];
   SEL methodSelector = NSSelectorFromString(methodName);
   NSArray *oc_args   = [args toArray];
   //調(diào)用給NSObject添加的方法,可以調(diào)用OC實(shí)例對象的方法
   id obj             = [ocObj js_performSelector:methodSelector withObjects:oc_args];
   return obj;
};
class NSObject{
    constructor(){
        //保存OC對象魁亦,相當(dāng)于強(qiáng)引用了指針
        this.ocPointer = null;
        //保存OC對象的類名渔隶,用于給OC反射創(chuàng)建一個OC實(shí)例
        this.ocClsName = 'NSObject';
    }
    ...
    
    //創(chuàng)建OC對象
    creatNative(){
        //調(diào)用OC方法,創(chuàng)建實(shí)例洁奈,并保存
        this.ocPointer = oc_creatObject(this.ocClsName.firstUpperCase());
    }
    ...
    
    //將JS對象綁定到OC對象
    bindJSValueToOC(){
        ...
        //執(zhí)行OC實(shí)例對象的相應(yīng)方法
        oc_invokeWithOneJSArg(this.ocPointer,'setJsValue:',this);
    }
    
    //調(diào)用OC
    invokeNative(method,...args){
        return oc_invokeWithArgs(this.ocPointer,method,args);
    }
}

視圖生成和層次關(guān)系解決

JS 里面接受JSON 傳遞給controller 實(shí)例间唉,調(diào)用controllerproduceSubviews 方法

    produceSubviews(){
        //this.components 就是獲取的JSON里面的components
        if(!this.components) return;
        this.components.forEach((item,index)=>{
            //這里把ListView反射,生成ListView實(shí)例
            let view = eval(`new ${item.type}()`);
            //把單個單個component(item)交給view
            view.initWithJSON(item);
            this.wrapperView.addSubview(view);
            this.viewStore.set(view.ocIdentify,view);
        });
    }

走到view.initWithJSON(item);的時候利术,view首先根據(jù)item這個對象設(shè)置好自己的屬性呈野,例如ocClsName等,再然后調(diào)用view.creatNative創(chuàng)建一個OC對象印叁,并自己保存在this.ocPointer被冒,其次再遍歷item里面的components創(chuàng)建子視圖军掂,這就是一個遞歸調(diào)用,這樣就解決了視圖的層級關(guān)系姆打。再添加子視圖this.addSubview(view)良姆,這個函數(shù)會調(diào)用OC的方法。這樣就已經(jīng)布局好了視圖幔戏。

生成并布局好了視圖后玛追,JSwrapperView交給SSBaseRenderController來進(jìn)行顯示。


JSON字符串中的變量和函數(shù)處理

主要得益于ES6的模板字符串的設(shè)計
我怎么把 "`${UI.screenW}`" 變成"375"呢闲延,解決方法可能方法比較笨痊剖。

let string = "`${UI.screenW}`";
string = "return" + string;
value = (new Function(string)).call();

這樣 "`${UI.screenW}`" 就變成了"375"

這樣函數(shù)也不難處理了仍然是通過改字符串并new Function(string)來解決。
由于JSON傳遞給JS后就直接變成了一個對象垒玲,這樣可以很容易對變量來進(jìn)行操作陆馁,也為數(shù)據(jù)流動的實(shí)現(xiàn)提供了可能。


數(shù)據(jù)的流動問題

難的是怎樣設(shè)計數(shù)據(jù)流動的形式合愈,我琢磨了很久叮贩。
最后決定使用“執(zhí)行Action”的形式來解決數(shù)據(jù)流動,參考了一下Redux佛析。把視圖變成一個狀態(tài)機(jī)益老,由狀態(tài)來決定視圖上面顯示的東西。

里面有很多細(xì)節(jié)處理寸莫,可以看源碼捺萌,有詳細(xì)的注釋,這里只是大致說一下原理膘茎。


聯(lián)系我

無論是否有疑問歡迎和我一起討論沒我會迅速回復(fù)你
地址:feelings0811@wutnew.net 或者 https://github.com/cx478815108

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桃纯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子披坏,更是在濱河造成了極大的恐慌态坦,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棒拂,死亡現(xiàn)場離奇詭異驮配,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)着茸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門壮锻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涮阔,你說我怎么就攤上這事猜绣。” “怎么了敬特?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵掰邢,是天一觀的道長牺陶。 經(jīng)常有香客問我,道長辣之,這世上最難降的妖魔是什么掰伸? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮怀估,結(jié)果婚禮上狮鸭,老公的妹妹穿的比我還像新娘。我一直安慰自己多搀,他們只是感情好歧蕉,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著康铭,像睡著了一般惯退。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上从藤,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天催跪,我揣著相機(jī)與錄音,去河邊找鬼夷野。 笑死叠荠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扫责。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼逃呼,長吁一口氣:“原來是場噩夢啊……” “哼鳖孤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抡笼,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤苏揣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后推姻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體平匈,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年藏古,在試婚紗的時候發(fā)現(xiàn)自己被綠了增炭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡拧晕,死狀恐怖隙姿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情厂捞,我是刑警寧澤输玷,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布队丝,位于F島的核電站,受9級特大地震影響欲鹏,放射性物質(zhì)發(fā)生泄漏机久。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一赔嚎、第九天 我趴在偏房一處隱蔽的房頂上張望膘盖。 院中可真熱鬧,春花似錦尽狠、人聲如沸衔憨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽践图。三九已至,卻和暖如春沉馆,著一層夾襖步出監(jiān)牢的瞬間码党,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工斥黑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留揖盘,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓锌奴,卻偏偏與公主長得像兽狭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鹿蜀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容