在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

在iOS中渲染vue與事件處理是什么

汽車電子技術 ? 來源:程序猿搬磚 ? 作者:程序猿搬磚 ? 2023-03-03 09:55 ? 次閱讀

上一節(jié)我們已經(jīng)完成了在iOS中集成vue,并成功拿到了創(chuàng)建Node的數(shù)據(jù)回調(diào),這一節(jié)我們來完成Node的建立與渲染,并完成事件支持。

「第一步: 定義Node節(jié)點的數(shù)據(jù)結構」

具體定義如下:

@interface DomNode : NSObject
/// DomNode的標識符
@property (nonatomic, copy)NSString *ref;
/// 節(jié)點的類型(這里暫時定義四種,滿足Demo的需要就可以了)
@property (nonatomic, assign)DomNodeType type;
/// 節(jié)點的渲染屬性,需要在渲染的時候展示出來的(其中有一部分是與布局屬性重合的:即在布局屬性里面也需要在渲染屬性里面)
@property (nonatomic, strong)DomAttribute *attribute;
/// 節(jié)點的布局屬性,用于Flex布局計算
@property (nonatomic, strong)DomStyle *style;
/// 父節(jié)點
@property (nonatomic, weak)DomNode *parent;
/// 子節(jié)點
@property (nonatomic, strong)NSMutableArray

在這個數(shù)據(jù)結構中DomStyle是用于參與布局計算的, DomAttribute用于渲染。

他們的具體數(shù)據(jù)結構如下:

@interface DomStyle : NSObject
@property (nonatomic, assign) YGDirection direction;
@property (nonatomic, assign) YGFlexDirection flexDirection;
@property (nonatomic, assign) YGJustify justifyContent;
@property (nonatomic, assign) YGAlign alignSelf;
@property (nonatomic, assign) YGAlign alignItems;
@property (nonatomic, assign) YGPositionType positionType;
@property (nonatomic, assign) YGWrap flexWrap;
@property (nonatomic, assign) YGOverflow overflow;
@property (nonatomic, assign) YGDisplay display;
@property (nonatomic, assign) int flex;
@property (nonatomic, assign) int flexGrow;
@property (nonatomic, assign) int flexShrink;
@property (nonatomic, assign) DomEdge position;
@property (nonatomic, assign) DomEdge margin;
@property (nonatomic, assign) DomEdge padding;
@property (nonatomic, strong) DomBorder *border;
@property (nonatomic, assign) CGFloat height;
@property (nonatomic, assign) CGFloat width;
@property (nonatomic, assign) CGFloat maxWidth;
@property (nonatomic, assign) CGFloat minWidth;
@property (nonatomic, assign) CGFloat maxHeight;
@property (nonatomic, assign) CGFloat minHeight;

- (instancetype)initWithData:(NSDictionary *)data;
- (void)updateStyleWithData:(NSDictionary * _Nullable)data;
- (void)fill:(YGNodeRef)ygNode;
@end

style中的數(shù)據(jù)結構比較簡單,需要注意的是在初始化相關屬性時,需要與Yoga定義的YGNodeRef中的數(shù)據(jù)結構初始化值一致,因為我們在fill方法會把所有支持的屬性全部同步到YGNodeRef

updateStyleWithDatainitWithData所傳遞進來的則是從vue中拿到的回調(diào)數(shù)據(jù),并將他們解析成對應的屬性值。

具體的實現(xiàn)代碼,我會附加在最后。

@interface DomAttribute : NSObject
@property (nonatomic, strong) NSString *color;
@property (nonatomic, strong) NSString *backgroundColor;
@property (nonatomic, assign) NSInteger fontSize;
@property (nonatomic, strong) NSString *fontFamily;
@property (nonatomic, strong) NSString *value;
@property (nonatomic, strong) NSString *imageNamed;
@property (nonatomic, assign) NSInteger maxNumberLine;
@property (nonatomic, strong) DomBorder *border;

- (instancetype)initWithData:(NSDictionary *)data;
- (void)updateAttributeWithData:(NSDictionary * _Nullable)data;
@end

這里需要注意的是,某些數(shù)據(jù)不僅參與計算,還參與渲染,比如: border

其他的數(shù)據(jù)結構定義的實現(xiàn)代碼,我會附加在最后。

「第二:構建渲染樹」

定義好Node所需要的數(shù)據(jù)結構之后,我們就可以將回調(diào)數(shù)據(jù)解析成一個Node Tree了。

- (void)_handleCallNativeCallback:(NSString *)instanceId data:(NSDictionary * _Nonnull)data {
    if(!data) return;
    NSDictionary *info = data[@"0"];
    if(!info || ![info isKindOfClass:[NSDictionary class]]) return;
    NSString *method = info[@"method"];
    if(method.length == 0) return;
    if([method isEqualToString:@"createBody"]) {
        [self _createBody:instanceId data:info];
    } else if([method isEqualToString:@"addElement"]) {
        [self _addElement:instanceId data:info];
    } else if([method isEqualToString:@"updateAttrs"]) {
        [self _updateAttrs:info];
    } else if([method isEqualToString:@"updateStyle"]) {
        [self _updateStyles:info];
    } else if([method isEqualToString:@"createFinish"]) {
        [self _createFinished];
    } else {
        NSLog(@"data: %@", data);
    }
}

具體方法實現(xiàn)代碼,附加在后面。

通過對callNative的處理,在createFinished時構建好Node Tree。

「第三:完成布局前的準備工作」

構建好Node Tree,就可以通知Yoga,可以開始計算布局了。

在通知Yoga之后,需要將屬性映射到YGNodeRef Tree

- (void)fill {
    [self.style fill:_ygNode];
    for(DomNode *child in _children) {
        [child fill];
    }
    _dirty = NO;
}

通過從根節(jié)點Node深度遍歷調(diào)用fill方法,將數(shù)據(jù)映射到YGNodeRef,這里需要注意的是,具體的fill方法是在style中實現(xiàn)的,因為只有style里面的屬性會參與計算。

具體的實現(xiàn)代碼如下:

- (void)fill:(YGNodeRef)ygNode {
    YGNodeStyleSetDirection(ygNode, _direction);
    YGNodeStyleSetDisplay(ygNode, _display);
    YGNodeStyleSetFlexDirection(ygNode, _flexDirection);
    YGNodeStyleSetJustifyContent(ygNode, _justifyContent);
    YGNodeStyleSetAlignSelf(ygNode, _alignSelf);
    YGNodeStyleSetAlignItems(ygNode, _alignItems);
    YGNodeStyleSetPositionType(ygNode, _positionType);
    YGNodeStyleSetFlexWrap(ygNode, _flexWrap);
    YGNodeStyleSetOverflow(ygNode, _overflow);
    YGNodeStyleSetFlex(ygNode, _flex);
    YGNodeStyleSetFlexGrow(ygNode, _flexGrow);
    YGNodeStyleSetFlexShrink(ygNode, _flexShrink);
    if(_width >= 0) YGNodeStyleSetWidth(ygNode, _width);
    if(_height >= 0) YGNodeStyleSetHeight(ygNode, _height);
    if(_minWidth >= 0) YGNodeStyleSetMinWidth(ygNode, _minWidth);
    if(_minHeight >= 0) YGNodeStyleSetMinHeight(ygNode, _minHeight);
    if(_maxWidth >= 0) YGNodeStyleSetMaxWidth(ygNode, _maxWidth);
    if(_maxHeight >= 0) YGNodeStyleSetMinWidth(ygNode, _maxHeight);
    YGNodeStyleSetBorder(ygNode, YGEdgeAll, _border.width);
    /// Padding
    if(self.padding.left >= 0)     YGNodeStyleSetPadding(ygNode, YGEdgeLeft, self.padding.left);
    if(self.padding.top >= 0)      YGNodeStyleSetPadding(ygNode, YGEdgeTop, self.padding.top);
    if(self.padding.right >= 0)    YGNodeStyleSetPadding(ygNode, YGEdgeRight, self.padding.right);
    if(self.padding.bottom >= 0)   YGNodeStyleSetPadding(ygNode, YGEdgeBottom, self.padding.bottom);
    /// Margin
    if(self.margin.left >= 0)      YGNodeStyleSetMargin(ygNode, YGEdgeLeft, self.margin.left);
    if(self.margin.top >= 0)       YGNodeStyleSetMargin(ygNode, YGEdgeTop, self.margin.top);
    if(self.margin.right >= 0)     YGNodeStyleSetMargin(ygNode, YGEdgeRight, self.margin.right);
    if(self.margin.bottom >= 0)    YGNodeStyleSetMargin(ygNode, YGEdgeBottom, self.margin.bottom);
    /// Position
    if(self.position.left >= 0)    YGNodeStyleSetPosition(ygNode, YGEdgeLeft, self.position.left);
    if(self.position.top >= 0)     YGNodeStyleSetPosition(ygNode, YGEdgeTop, self.position.top);
    if(self.position.right >= 0)   YGNodeStyleSetPosition(ygNode, YGEdgeRight, self.position.right);
    if(self.position.bottom >= 0)  YGNodeStyleSetPosition(ygNode, YGEdgeBottom, self.position.bottom);
}

構建好YGNodeRef Tree之后就可以進行布局的計算了

CGSize screenSize = self.view.bounds.size;
YGNodeCalculateLayout(ygNode, screenSize.width, screenSize.height, YGNodeStyleGetDirection(ygNode));

通過調(diào)用以上接口,計算好每個元素的位置與大小。

這里需要注意的是,screenSize并不是一定要傳遞屏幕大小,我們需要渲染到的目標視圖是多大,就傳遞多大。

在這里我們剛好使用了整個屏幕

「第四:開始渲染」

完成布局計算后,就開始對Node進行渲染了,代碼很簡單:

由于是測試代碼,所以只是簡單的完成了渲染,沒有進行優(yōu)化。

實際上這里應該將不同節(jié)點在原生對應的元素定義出來,通過元素內(nèi)部的方法進行循環(huán)渲染,使代碼結構更簡單。

- (void)_render:(DomNode *)node superView:(UIView *)superView {
    if(!node) return;
    for(DomNode *child in node.children) {
        UIView *childView = NULL;
        if(child.type == DomNodeTypeLabel) {
            UILabel *label = [[UILabel alloc] init];
            label.font = [UIFont systemFontOfSize:child.attribute.fontSize];
            label.textColor = [UIColor colorWithHexString:child.attribute.color alpha:1.0f];
            label.text = child.attribute.value;
            childView = label;
        } else if(child.type == DomNodeTypeView) {
            UIView *view = [[UIView alloc] init];
            view.backgroundColor = [UIColor colorWithHexString:child.attribute.backgroundColor alpha:1.0f];
            childView = view;
        } else if(child.type == DomNodeTypeButton) {
            UIButton *button = [[UIButton alloc] init];
            [button setTitle:child.attribute.value forState:UIControlStateNormal];
            [button setTitleColor:[UIColor colorWithHexString:child.attribute.color alpha:1.0f] forState:UIControlStateNormal];
            button.titleLabel.font = [UIFont systemFontOfSize:child.attribute.fontSize];
            childView = button;
        }
        childView.frame = child.rect;
        childView.backgroundColor = [UIColor colorWithHexString:child.attribute.backgroundColor alpha:1.0f];
        [superView addSubview:childView];
        childView.node = child;
        if(child.events.count > 0) {
            for(NSString *event in child.events) {
                if([event isEqualToString:@"click"]) {
                    childView.userInteractionEnabled = YES;
                    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_clickEvent:)];
                    [childView addGestureRecognizer:tap];
                }
            }
        }
        
        if(child.children.count > 0) {
            [self _render:child superView:childView];
        }
    }
}

完成渲染之后,是這樣一個效果:

圖片

「第五:處理事件」

樣式的渲染不是一層不變的,最容易想到的就是事件會改變數(shù)據(jù)的狀態(tài),那么事件怎么傳遞給vue呢。

vue-weex-framework在加載之后,會在globalObject上掛載一個方法__WEEX_CALL_JAVASCRIPT__,通過JSContext來調(diào)用這個方法,將事件與事件掛載的元素id傳遞過去,就完成了在vue內(nèi)部的事件調(diào)用。

代碼如下:

- (void)sendEvent:(NSString *)ref event:(NSString *)event {
    NSLog(@"IOS Context收到事件: %@, %@", ref, event);
    NSDictionary *params = @{
        @"module": @"",
        @"method": @"fireEvent",
        @"args": @[
            ref,
            event
        ]
    };
    NSArray *args = @[@"1", @[params]];
    [[_context globalObject] invokeMethod:@"__WEEX_CALL_JAVASCRIPT__" withArguments:args];
}

完成了事件的渲染,我們來看看具體的效果

圖片

**「這里有一個點需要注意一下:

1.當數(shù)據(jù)發(fā)生變化的時候,怎么讓原生感知它的變化呢,這里我使用了CADisplayLink,每一幀都去檢測一下Node Tree是否已經(jīng)發(fā)生改變,如果有節(jié)點發(fā)生改變,就需要重新計算。

慶幸的是Yoga在內(nèi)部是有緩存的,當我們標記了某一個節(jié)點需要重新計算后,Yoga會去判斷哪些相關節(jié)點需要重新計算,不需要計算的則不會再計算了。

這樣就會大大減少數(shù)據(jù)更新計算布局的時間了。

2.如果使用div來顯示文本,在數(shù)據(jù)發(fā)生改變時不會調(diào)用updateAttrs,需要使用text標簽顯示會發(fā)生改變的文本信息

」**

到這里,我們基本上完成了從vue到渲染成原生的所有步驟,當然里面還有一些細節(jié)是沒有處理好的,比如在加載vue模板的時候還可以傳遞一個json數(shù)據(jù)進去作為從原生代入的初始數(shù)據(jù)。

整體的骨架已經(jīng)有了,感興趣的朋友優(yōu)化骨架完善細節(jié)就是接下來。

「總結:」

這個小系列分為三個小節(jié),實例了一個有基本骨架結構的渲染vue代碼的引擎:

1.完成從vue開發(fā)到打包成非瀏覽器環(huán)境使用的代碼,完成vue-js-framework打包

2.將打包好的framework與vue模板代碼集成到iOS當中

3.完成渲染與事件處理

寫到最后:

本文章以iOS平臺為宿主環(huán)境,很容易的你能想到將這個引擎擴展到android,或者更多的平臺。

「附加資料:」

iOS-Vue-Demo: https://github.com/czqasngit/iOS-Vue-Demo

vue: https://cn.vuejs.org/

weex-framework:

https://github.com/apache/incubator-weex

webpack:

https://webpack.js.org/

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • iOS
    iOS
    +關注

    關注

    8

    文章

    3399

    瀏覽量

    152140
  • node
    +關注

    關注

    0

    文章

    24

    瀏覽量

    6054
  • vue
    vue
    +關注

    關注

    0

    文章

    58

    瀏覽量

    8060
收藏 人收藏

    評論

    相關推薦

    用WEB技術棧開發(fā)NATIVE應用(二):WEEX 前端SDK原理詳解

    的整體架構:可以看到JS-Native Bridge將渲染指令發(fā)送給Android或者iOS渲染引擎之前,我們的業(yè)務代碼運行在JSCore/v8的執(zhí)行引擎之中,而在該執(zhí)行引擎之中除了
    發(fā)表于 03-02 14:10

    Linux搭建Vue開發(fā)環(huán)境

    本文介紹Linux環(huán)境下從零開始搭建Vue開發(fā)環(huán)境的整個過程,包括vue的安裝,webstorm 安裝配置,devtools的安裝。
    發(fā)表于 07-24 06:20

    12vue的插槽

    12vue插槽(slot)
    發(fā)表于 05-07 08:15

    vue的路由router是什么

    vue的路由router
    發(fā)表于 05-20 07:10

    Vue父組件與子組件之間的數(shù)據(jù)傳遞

    Vue父組件(vue實例)與子組件(component)之間的數(shù)據(jù)傳遞
    發(fā)表于 06-01 17:28

    vue-cli-----vue實例template:'<App/>是什么意思?

    哪位大神知道vue-cli-----vue實例template:'是什么意思嗎?
    發(fā)表于 11-05 07:02

    vue全局變量的設置與組件修改全局變量的方法?

    vue全局變量的設置與組件修改全局變量的方法
    發(fā)表于 11-06 06:43

    前端渲染引擎的優(yōu)勢分析

    React、Vue、Angular等均屬于MVVM模式,一些只需完成數(shù)據(jù)和模板簡單渲染的場合,顯得笨重且學習成本較高,而解決該問題非常優(yōu)秀框架之一是doT.js,本文將對它進行詳解。 背景 前端
    發(fā)表于 09-30 13:14 ?0次下載
    前端<b class='flag-5'>渲染</b>引擎的優(yōu)勢分析

    新iPhone和iOS13的3D渲染圖搶先看_ios13或將新增夜間模式

    外網(wǎng)PhoneAena聯(lián)合設計師,綜合之前的網(wǎng)曝信息,制作了新iPhone和iOS13的3D渲染圖。制作渲染
    的頭像 發(fā)表于 03-16 10:52 ?3383次閱讀
    新iPhone和<b class='flag-5'>iOS</b>13的3D<b class='flag-5'>渲染</b>圖搶先看_<b class='flag-5'>ios</b>13或將新增夜間模式

    Vue入門Vue的生命周期

    .生命周期 4.1生命周期是什么 Vue的生命周期, 就是Vue實例從創(chuàng)建到銷毀的過程.
    的頭像 發(fā)表于 02-06 16:16 ?974次閱讀
    <b class='flag-5'>Vue</b>入門<b class='flag-5'>Vue</b>的生命周期

    Vue入門之Vue定義

    Vue (讀音 /vju?/,類似于 view) 是一套用于構建用戶界面的漸進式JavaScript框架。 Vue 的核心庫只關注視圖層,也就是只處理頁面。 Vue提供的一套J
    的頭像 發(fā)表于 02-06 16:41 ?1215次閱讀
    <b class='flag-5'>Vue</b>入門之<b class='flag-5'>Vue</b>定義

    iOS中集成Vue是什么

    上一節(jié)Vue非瀏覽器環(huán)境下的嘗試我們利用了weexvue的dom實現(xiàn)成功的非瀏覽器環(huán)境
    的頭像 發(fā)表于 03-03 09:56 ?778次閱讀
    <b class='flag-5'>在</b><b class='flag-5'>iOS</b>中集成<b class='flag-5'>Vue</b>是什么

    簡單介紹一下Vue的響應式原理

    自從 Vue 發(fā)布以來,就受到了廣大開發(fā)人員的青睞,提到 Vue,我們首先想到的就是 Vue 的響應式系統(tǒng),那響應式系統(tǒng)到底是怎么回事呢?
    的頭像 發(fā)表于 03-13 10:11 ?884次閱讀

    簡述大前端技術棧的渲染原理

    應用開發(fā):Android、iOS、鴻蒙(HarmonyOS)等; ?Web前端框架:Vue、React、Angular等; ?小程序開發(fā):微信小程序、京東小程序、支付寶小程序等; ?跨平臺解決方案:React Native、Flutter、Taro、Weex等。 什么是
    的頭像 發(fā)表于 11-07 10:11 ?477次閱讀

    Vue3設計思想及響應式源碼剖析

    DOM進行了重寫、對模板的編譯進行了優(yōu)化操作... 2、Vue3設計思想 ?Vue3.0更注重模塊上的拆分,2.0無法單獨使用部分模塊。需要引入
    的頭像 發(fā)表于 12-20 10:24 ?329次閱讀
    主站蜘蛛池模板: 四虎海外在线永久免费看 | 四虎一区二区三区精品 | www.色亚洲| 久操资源在线 | 窝窝午夜看片成人精品 | 日本人69xxⅹ69 | www.色日本| 亚洲不卡免费视频 | 午夜精品视频在线观看美女 | 色五月在线视频 | 在线观看视频免费 | 91美女在线播放 | 高h污快穿文汁水四溅 | 日本成本人三级在线观看2018 | 色www亚洲国产张柏芝 | 免费一级欧美在线观看视频片 | 一级毛片a | 成人a毛片免费全部播放 | 一区二区三区欧美在线 | 主人扒开腿揉捏花蒂调教cfh | 99国产在线 | 天堂种子 | 奇米777me | 午夜影视啪啪免费体验区深夜 | 婷婷网址| 2345成人高清毛片 | 日本大片免费一级 | 国产黄mmd在线观看免费 | 天堂最新版免费观看 | 国产亚洲精品久久久久久久软件 | 成年视频xxxxx免费播放软件 | 日韩精品视频免费在线观看 | 成年人网站黄色 | 永久免费影视在线观看 | 福利视频免费看 | 四虎h789fcom | 一级一黄在线观看视频免费 | 男人和女人做爽爽视频在线观看 | 亚洲国产成a人v在线观看 | 快色视频免费 | 天天色天天色 |