iOS調(diào)試Demo
WeexDemo
本篇將開始跟大家探討如何進行Weex頁面跟原生的交互,即Weex調(diào)原生方法窃爷,原生方法調(diào)取Weex方法姓蜂。
-------------2017.05.25 今晚寫 敲了一天了--------
-------------2017.05.26 胳膊疼 家里電腦什么環(huán)境也沒弄逮京,今晚就寫了------------
像Weex提供的navigator,animate等擴展都是通過兩者交互實現(xiàn)的造虏。
接第二篇的頁面實現(xiàn)麦箍。
從Weex的擴展開始
如何實現(xiàn),點擊Push
新的頁面陶珠?Weex
提供了navigator
擴展挟裂。
來看WXNavigatorModule
源碼。
首先他遵守WXModuleProtocol
協(xié)議
@protocol WXModuleProtocol <NSObject>
/*多用于 Module 回調(diào)結(jié)果給 js揍诽,回調(diào)類型分為下面兩種:
WXModuleCallback 為了性能考慮诀蓉,該回調(diào)只能回調(diào)通知js一次,之后會被釋放暑脆,多用于一次結(jié)果
WXModuleKeepAliveCallback 該回調(diào)可以設(shè)置是否為多次回調(diào)類型渠啤,多次回調(diào)的場景如持續(xù)監(jiān)聽位置的變化,并返回給 js
*/
/**
這個是聲明了一個通用的Native回調(diào)JS的block 這個會在使用完之后被釋放以來節(jié)約內(nèi)存 */
typedef void (^WXModuleCallback)(id result);
/**
這個是聲明了一個通用的Native回調(diào)JS的block 這個會通過keepAlive參數(shù)來決定 使用完之后被釋放以來節(jié)約內(nèi)存
*/
typedef void (^WXModuleKeepAliveCallback)(id result, BOOL keepAlive);
/**
該Moudule綁定的Instance添吗。我們可以通過他在xxxModule.m來調(diào)用方法和屬性
*/
@property (nonatomic, weak) WXSDKInstance *weexInstance;
@end
上述協(xié)議當我們要擴展一個Module
時僵腺,我們需要遵守這個協(xié)議。在xxxxModule.m
中需要@synthesize weexInstance
生成成員變量。
那么我們?nèi)绾螌⒃椒ū┞督oJS呢呕童?通過WX_EXPORT_METHOD(@selector(xxxxx))
這個宏來實現(xiàn)往声。
如WXNavigatorModule
有一個方法:
- (void)open:(NSDictionary *)param success:(WXModuleCallback)success failure:(WXModuleCallback)failure
{
}
那么就可以通過
WX_EXPORT_METHOD(@selector(open:success:failure:))
暴露給JS慢洋。
那么WX_EXPORT_METHOD
是如何實現(xiàn)的呢隘马?
#define WX_EXPORT_METHOD(method) WX_EXPORT_METHOD_INTERNAL(method,wx_export_method_)
#define WX_EXPORT_METHOD_INTERNAL(method, token) \
+ (NSString *)WX_CONCAT_WRAPPER(token, __LINE__) { \
return NSStringFromSelector(method); \
}
#define WX_CONCAT_WRAPPER(a, b) WX_CONCAT(a, b)
#define WX_CONCAT(a, b) a ## b
如上,首先會傳遞過來一個SEL
參數(shù),然后將wx_export_method_
和當前代碼行數(shù)
拼接成一個方法名如改宏寫在第69行:wx_export_method_69
具體方法為:
+ (NSString *)wx_export_method_69 {
return NSStringFromSelector(method);
}
實現(xiàn)一個簡單需求
了解了如何擴展Module
叛氨,那我們現(xiàn)在就從最簡單的需求開始仁连,點擊某一行Cell
Push一個新的界面昌抠。
再來看WXNavigatorModule
- (id<WXNavigationProtocol>)navigator
{
id<WXNavigationProtocol> navigator = [WXHandlerFactory handlerForProtocol:@protocol(WXNavigationProtocol)];
return navigator;
}
- (void)open:(NSDictionary *)param success:(WXModuleCallback)success failure:(WXModuleCallback)failure
{
//這里 Weex源碼中 使用了一個有默認實現(xiàn)WXNavigationProtocol的協(xié)議類冰沙,
//并將它注冊到WXHandlerFactory實例的handlers(這是一個線程安全字典:WXThreadSafeMutableDictionary 繼承自NSMutableDictionary 目的是為了達到讀寫都在同一個并發(fā)隊列)
id<WXNavigationProtocol> navigator = [self navigator];
UIViewController *container = self.weexInstance.viewController;
if (navigator && [navigator respondsToSelector:@selector(open:success:failure:withContainer:)]) {
//這樣通過注冊的默認實現(xiàn)來響應(yīng)這個方法 這里我們可以不去考慮他的內(nèi)部實現(xiàn),依照我們平時Push的方法書寫下面代碼就可以
[navigator open:param success:success failure:failure withContainer:container];
}
}
weex 幫我們實現(xiàn)了一個常用的配置導(dǎo)航欄的接口:
到這里我們已經(jīng)看到了Weex的
WXNavigatorModule
是如何開放接口給JS,現(xiàn)在我們就可以自己寫一個Module
現(xiàn)在我們自己擴展一個
Module
才睹,以iOS調(diào)試Demo中的XMWXModule為例,我們自己實現(xiàn)一個Push新頁面的方法。
WX_EXPORT_METHOD(@selector(openURL:options:completionHandler:))
-(void)openURL:(NSString *)url options:(NSDictionary<NSString *,id> *)options completionHandler:(WXCallback)completion
{
NSString *newURL = url;
if ([url hasPrefix:@"http://"]) {
newURL = [NSString stringWithFormat:@"http:%@", url];
} else if (![url hasPrefix:@"http"]) {
newURL = [NSURL URLWithString:url relativeToURL:self.weexInstance.scriptURL].absoluteString;
}
XMWXViewController * controller = [[XMWXViewController alloc] init];
controller.renderURL = [NSURL URLWithString:newURL];
//從option參數(shù)中去取出navigtionBarInfo的數(shù)據(jù)
if ([options objectForKey:@"navigtionBarInfo"]) {
controller.renderInfo = [XMWXNavigationItem infoWithDict:[options objectForKey:@"navigtionBarInfo"]];
}
[self.weexInstance.viewController showViewController:controller sender:nil];
completion(@{@"result":@"success"});
}
到這第一步的擴展Module
已經(jīng)可以了忍啤,現(xiàn)在就去注冊我們的Module
,在Weex配置方法中加入
[WXSDKEngine registerModule:@"XMWXModule" withClass:NSClassFromString(@"XMWXModule")];
如何使用擴展Module
以個人主頁的ViewController為例FifthViewController.vue
Weex 從無到有開發(fā)一款上線應(yīng)用 2中已經(jīng)說了如何做一個Cell,現(xiàn)在我們就為Cell加上點擊事件,調(diào)取我們的方法
實現(xiàn)如下:
<script>
//如何實現(xiàn)點擊事件冤狡,
//1. 首先引入我們在原生注冊的Module名字
let appBasicModule = weex.requireModule('XMWXModule')
export default {
props: {
// cell的Model數(shù)據(jù)
item: {
type: Object,
default: 'null'
},
// 因為點贊和余額的DetailTextLabel的顏色不一樣,需要給其一個判斷條件
isMark: {//這個cell是不是點贊Cell
type: Boolean,
default: false
}
},
data () {
return {}
},
methods: {
// 2.實現(xiàn)點擊事件方法
goPage()
{
// 3.配置導(dǎo)航欄信息
var navigtionBarInfo = {
title: this.item.navigaitonBarTitle,
clearTitleColor:'333333',
blurTitleColor:'333333',
clearNavigationBar:true,
hiddenNavgitionBar:false,
navigationBarBackgroundColor:'',
navgationBarBackgroundImage:'',
customTitleViewURL:'',
};
// 4.調(diào)用原生方法
appBasicModule.openURL(this.item.actionUrl,{//這里的actionUrl就是我們每一個ViewController對應(yīng)的JSBundle文件地址
navigtionBarInfo:navigtionBarInfo,
},function (result) {
})
}
}
}
</script>
現(xiàn)在我們的個人主頁翅阵,可以點擊然后Push 到新的頁面滥崩。效果如下:
取JS
先聊需求,假設(shè)我們iOS有一個比較好的下拉加載控件茸时,但是安卓的同事沒能實現(xiàn)出來旋炒,那么我們無法將這個擴展成Component(因為兩端不通用),那我們該如何操作這部分邏輯呢?首先睁壁,考慮到我們當前的頁面一定是Weex實現(xiàn)的,那么網(wǎng)絡(luò)請求也一定是在Vue文件中。所以當原生的刷新控件要調(diào)用beginRefresh
是就需要調(diào)用JS的refresh
方法盐杂。
先暴露JS方法給Native.
<script>
// 1 獲取全局響應(yīng)Module
let globalEvent = weex.requireModule('globalEvent');
created () {
let self = this;
self.day = new Date().getDate();
this.host=this.getHost().replace('8081','8083')
try {
appBasicModule.accessKeyWithCallback(function (accessKey) {
self.accessKey = accessKey;
});
appBasicModule.userIdWithCallback(function (userId) {
self.userId = userId
self.getAllData();
})
}
catch (e) {
self.getAllData();
}
},
mounted(){
let self = this;
// 監(jiān)聽調(diào)取JS的信號名,類似于通知中心 這個地方就是注冊原生發(fā)送的refresh通知
// refresh就是原生的通知名眉尸,后邊跟著的就是需要收到通知需要做的事情
globalEvent.addEventListener("refresh", function (e) {
// 刷新頁面 重新請求數(shù)據(jù)等
});
}
</script>
原生端需要使用這些方法:
/**
* @abstract Fire an event to the component and tell Javascript which value has been changed.
* @param eventName 事件名稱慢宗,可以在weex文件某個標簽組件監(jiān)聽嘴脾,命名規(guī)范為 onXXX
* @param params 數(shù)據(jù)
* @param domChanges 發(fā)生改變的數(shù)據(jù)
**/
- (void)fireEvent:(NSString *)eventName params:(NSDictionary *)params domChanges:(NSDictionary *)domChanges
/**
* fire module event;
*/
- (void)fireModuleEvent:(Class)module eventName:(NSString *)eventName params:(NSDictionary*)params;
/**
* fire global event
*/
- (void)fireGlobalEvent:(NSString *)eventName params:(NSDictionary *)params;
Weex文檔有這方面描述结澄。
這里補充的是用實例Instance
和globalEvent
來操作旅赢。
//添加刷新
if (!scrollView.mj_header) {
scrollView.mj_header = [HLCustomRefreshHeader headerWithRefreshingBlock:^{
//調(diào)取JS刷新方法
if (self.segmentedControl.selectedSegmentIndex == 0) {
[self.guanzhuInstance fireGlobalEvent:@"refresh" params:nil];
}else
{
[self.guangchangInstance fireGlobalEvent:@"refresh" params:nil];
}
resetScrollViewSomePro(scrollView);
}];
indestance.endRefreshBlock = ^{
@strongify(scrollView);
if (scrollView.mj_header.state != MJRefreshStateIdle) {
[scrollView.mj_header setState:MJRefreshStateIdle];
resetScrollViewSomePro(scrollView);
}
};
這樣就達到了Native調(diào)用JS的目的(Demo中沒有使用這種方式悠就,這是我們公司應(yīng)用針對需求寫的邏輯,可以下載App看一下:Applestore 搜索:葫蘆知識)妈嘹。