1. 程式人生 > >H5與iOS互動

H5與iOS互動

- (void)webViewDidFinishLoad:(UIWebView *)webView
{

    //js方法名+引數
    NSString* jsCode = [NSString stringWithFormat:@"report('%@')",self.jsStr];

    //呼叫html頁面的js方法
    [webView stringByEvaluatingJavaScriptFromString:jsCode];

}

資料:
1 原理
在寫 JavaScript 的時候,可以使用一個叫做 window 的物件,像是我們想要從現在的網頁跳到另外一個網頁的時候,就會去修改 window.location.href 的位置;在我們的 Objective-C 程式碼中,如果我們可以取得指定的 WebView 物件,也就可以拿到這個出現在 JavaScript 中的 window 物件,也就是 [webView windowScriptObject]。

這個物件就是 WebView 裡頭的 JS 與我們的 Objective-C程式之間的橋樑——window 物件可以取得網頁裡頭所有的 JS 函式與物件,而如果我們把一個 Objective-C 物件設定成 windowScriptObject 的 value,JS 也便可以呼叫Objective-C物件的 method。於是,我們可以在Objective-C 程式裡頭要求 WebView 執行一段 JS,也可以反過來讓 JS 呼叫一段用 Obj C 實現的功能。

由於Objective-C 與 JS 本身的語言特性不同,在兩種語言之間相互傳遞東西之間,就可以看到兩者的差別:

JS 雖然是 OO,但是並沒有 class,所以將 JS 物件傳到 Obj C 程式裡頭,除了基本字串會轉換成 NSString、基本數字會轉成 NSNumber,像是 Array 等其他物件,在 Objective-C 中,都是 WebScriptObject 這個 Class。意思就是,JS 的 Array 不會幫你轉換成 NSArray。從 JS 裡頭傳一個空物件給 Objective-C 程式,用的不是 Objective-C 裡頭原本表示「沒有東西」的方式,像是 NULL、nil、NSNull 等,而是專屬 WebKit 使用的 WebUndefined。
2 具體技巧

2.1 用Objective-C 取得與設定JavaScript 物件

要從 Objective-C取得網頁中的 JavaScript 物件,也就是對 windowScriptObject 做一些 KVC 呼叫,像是 valueForKey: 與 valueForKeyPath:。如果我們在 JS 裡頭,想要知道目前的網頁位置,會這麼寫:

var location = window.location.href;

用 Objective-C 就可以這麼呼叫:

NSString *location = [[webView windowScriptObject] valueForKeyPath:@location
.href];

如果我們要設定 window.location.href,要求開啟另外一個網頁,在 JS 裡頭:

window.location.href = 'http://spring-studio.net';

在Objective-C:

[[webView windowScriptObject] setValue:@http://spring-studio.netforKeyPath:@location.href];

2.2 用Objective C 呼叫 JavaScript function

2.2.1 用 evaluateWebScript: 執行
要用 Objective-C 呼叫網頁中的 JS function,大概有幾種方法。第一種是直接寫一段跟你在網頁中會撰寫的 JS 一模一樣的程式,叫 windowScriptObject 用 evaluateWebScript: 執行。

例如,我們想要在網頁中產生一個新的 JS function,內容是:

function x(x) {

return x + 1;

}

所以在 Objective-C 中可以這樣寫;

[[webView windowScriptObject] evaluateWebScript:@function x(x) { return x + 1;}];

接下來我們就可以呼叫 window.x():

NSNumber *result = [[webView windowScriptObject] evaluateWebScript:@x(1)];
NSLog(@result:%d, [result integerValue]); // Returns 2

2.2.2 用function物件WebScriptObject執行自己
由於在 JS 中,每個 funciton 其實都是物件,所以我們還可以直接取得 window.x 叫這個物件執行自己。

在 JS 裡頭如果這樣寫:

window.x.call(window.x, 1);

Objective-C 中便是這樣:

WebScriptObject *x = [[webView windowScriptObject] valueForKey:@x];
NSNumber *result = [x callWebScriptMethod:@call withArguments:[NSArray arrayWithObjects:x, [NSNumbernumberWithInt:1], nil]];

這種讓某個 WebScriptObject 自己執行自己的寫法,其實比較不會用於從 Objective-C 呼叫 JS 這一端,而是接下來會提到的,由 JS 呼叫 Objective-C,因為這樣 JS 就可以把一個 callback function 送到 Objective-C 程式裡頭。

2.2.3 利用DOM物件
WebKit 裡頭,所有的 DOM 物件都繼承自 DOMObject,DOMObject 又繼承自 WebScriptObject,所以我們在取得了某個 DOM 物件之後,也可以從 Objective-C 程式中,要求這個 DOM 物件執行 JS 程式。

假如我們的網頁中,有一個 id 叫做 “#s” 的文字輸入框(text input),而我們希望現在鍵盤輸入的焦點放在這個輸入框上,在 JS 裡頭會這樣寫:

document.querySelector(‘#s’).focus();

在Objective-C中寫法:

DOMDocument *document = [[webView mainFrame] DOMDocument];
[[document querySelector:@#s] callWebScriptMethod: @focuswithArguments:nil];

2.3 用JavaScript存取Objective-C的Value

要讓網頁中的 JS 程式可以呼叫 Objective-C 物件,方法是把某個 Objective-C 物件註冊成 JS 中 window 物件的屬性。之後,JS 便也可以呼叫這個物件的 method,也可以取得這個物件的各種 Value,只要是 KVC 可以取得的 Value,像是 NSString、NSNumber、NSDate、NSArray、NSDictionary、NSValue…等。JS 傳 Array 到 Objective-C 時,還需要做些特別處理才能變成 NSArray,從 Obj C 傳一個 NSArray 到 JS 時,會自動變成 JS Array。

2.3.1 將Objective-C物件註冊給window物件的時機
首先我們要注意的是將 Objective-C 物件註冊給 window 物件的時機,由於每次重新載入網頁,window 物件的內容都會有所變動-畢竟每個網頁都會有不同的 JS 程式,所以,我們需要在適當的時機做這件事情。我們首先要指定 WebView 的 frame loading delegate(用 setFrameLoadDelegate:),並且實現 webView:didClearWindowObject:forFrame:,WebView 只要更新了 windowScriptObject,就會呼叫這一段程式。

假如我們現在要讓網頁中的 JS 可以使用目前的 controller 物件,會這樣寫:

- (void)webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)windowObject forFrame:(WebFrame *)frame

{

[windowObject setValue:self forKey:@controller];

}

如此一來,只要呼叫 window.controller,就可以呼叫我們的 Objective-C 物件。

2.3.2 JS中存取 Objective-C 物件的 Value
假如我們的 Objective-C Class 裡頭有這些成員變數:

@interface MyController : NSObject

{

IBOutlet WebView *webView;

IBOUtlet NSWindow *window;

NSString *stringValue;

NSInteger numberValue;

NSArray *arrayValue;

NSDate *dateValue;

NSDictionary *dictValue;

NSRect frameValue;

}

@end

指定一下 Value:

stringValue = @string;

numberValue = 24;

arrayValue = [[NSArray arrayWithObjects:@text, [NSNumbernumberWithInt:30], nil] retain];

dateValue = [[NSDate date] retain];

dictValue = [[NSDictionary dictionaryWithObjectsAndKeys:@value1,@key1, @value2, @key2, @value3, @key3, nil] retain];

frameValue = [window frame];

用 JS 讀讀看:

var c = window.controller;

var main = document.getElementById('main');

var HTML = '';

if (c) {

HTML += '

' + c.stringValue + '

';

HTML += '

' + c.numberValue + '

';

HTML += '

' + c.arrayValue + '

';

HTML += '

' + c.dateValue + '

';

HTML += '

' + c.dictValue + '

';

HTML += '

' + c.frameValue + '

';

main.innerHTML = HTML;

}

結果如下:

string 24 text,30 2010-09-09 00:01:04 +0800 { key1 = value1; key2 = value2; key3 = value3; } NSRect: {{275, 72}, {570, 657}}

不過,如果你看完上面的範例,就直接照做,應該不會直接成功出現正確的結果,而是會拿到一堆 undefined,原因是,Objective-C 物件的 Value 預設被保護起來,不會讓 JS 直接存取。要讓 JS 可以存取 Objective-C 物件的 Value,需要操作 +isKeyExcludedFromWebScript: 針對傳入的 Key 一一處理,如果我們希望 JS 可以存取這個 key,就回傳 NO:

+ (BOOL)isKeyExcludedFromWebScript:(const char *)name

{

if (!strcmp(name, stringValue)) {

return NO;

}

return YES;

}

除了可以讀取 Objective-C物件的 Value 外,也可以設定 Value,相當於在 Objective-C中使用 setValue:forKey:,如果在上面的 JS 程式中,我們想要修改 stringValue,直接呼叫 c.stringValue = ‘new value’ 即可。像前面提到,在這裡傳給 Objective-C的 JS 物件,除了字串與數字外,class 都是 WebScriptObject,空物件是 WebUndefined。

2.4 用 JavaScript呼叫 Objective-C方法

2.4.1 方法寫法差異
Objective-C 的語法沿襲自 SmallTalk,Objective-C 的 selector,與 JS 的 function 語法有相當的差異。WebKit 預設的實事是,如果我們要在 JS 呼叫 Objective-C selector,就是把所有的引數往後面擺,並且把所有的冒號改成底線,而原來 selector 如果有底線的話,又要另外處理。

假使我們的 controller 物件有個 method,在 Objective-C 中寫成這樣:

  • (void)setA:(id)a b:(id)b c:(id)c;

在 JS 中就這麼呼叫:

controller.setA_b_c_(‘a’, ‘b’, ‘c’);

2.4.2 給方法取別名
實在有點醜。所以 WebKit 提供一個方法,可以讓我們把某個 Objective-C selector 變成好看一點的 JS function。我們要實現 webScriptNameForSelector:

+ (NSString *)webScriptNameForSelector:(SEL)selector

{

if (selector == @selector(setA:b:c:)) {

return @setABC;

}

return nil;

}

以後就可以這麼呼叫:

controller.setABC(‘a’, ‘b’, ‘c’);

我們同樣可以決定哪些 selector 可以給 JS 使用,哪些要保護起來,方法是實作 isSelectorExcludedFromWebScript:。而我們可以改變某個 Objective-C selector 在 JS 中的名稱,我們也可以改變某個 value 的 key,方法是實作 webScriptNameForKey:。

2.4.3 注意
有幾件事情需要注意一下:

用 JavaScript 呼叫 Objective C 2.0 的 property。在上面,我們用 JS 呼叫 window.controller.stringValue,與設定裡頭的 value 時,這邊很像我們使用 Objective-C 2.0 的語法,但其實做的是不一樣的事情。用 JS 呼叫 controller.stringValue,對應到的 Objective-C 語法是 [controller valueForKey:@stringValue],而不是呼叫 Objective-C 物件的 property。

如果我們的 Objective-C 物件有個 property 叫做 stringValue,我們知道,Objective-C property 其實會在編譯時,變成 getter/setter method,在 JS 裡頭,我們便應該要呼叫 controller.stringValue() 與 controller.setStringValue_()。

Javascript 中,Function 即物件的特性

JS 的 function 是物件,當一個 Objective-C 物件的 method 出現在 JS 中時,這個 method 在 JS 中,也可以或多或少當做物件處理。我們在上面產生了 setABC,也可以試試看把它倒出來瞧瞧:

console.log(controller.setABC);

我們可以從結果看到:

function setABC() { [native code] }

這個 function 是 native code。因為是 native code,所以我們無法對這個 function 呼叫 call 或是 apply。

另外,在把我們的 Objective-C 物件註冊成 window.controller 後,我們會許也會想要讓controller變成一個 function 來執行,像是呼叫 window.controller();或是,我們就只想要產生一個可以讓 JS 呼叫的 function,而不是整個物件都放進 JS 裡頭。我們只要在 Objective-C 物件中,實現invokeDefaultMethodWithArguments:,就可以回傳在呼叫 window.controller() 時想要的結果。

2.4.4 示例
現在我們可以綜合練習一下。前面提到,由於我們可以把 JS 物件以 WebScriptObject 這個 class 傳入 Obj C 程式,Objective-C 程式中也可以要求執行 WebScriptObject 的各項 function。我們假如想把 A 與 B 兩個數字丟進 Objective-C 程式裡頭做個加法,加完之後出現在網頁上,於是我們寫了一個 Objective-C method:

- (void)numberWithA:(id)a plusB:(id)b callback:(id)callback

{

NSInteger result = [a integerValue] + [b integerValue];

[callback callWebScriptMethod:@call withArguments:[NSArrayarrayWithObjects:callback, [NSNumber numberWithInteger:result],nil]];

}

JS 裡頭就可以這樣呼叫:

window.controller.numberWithA_plusB_callback_(1, 2,function(result) {

var main = document.getElementById('main');

main.innerText = result;

});

3 WebViewJavascriptBridge橋接庫

WebViewJavascriptBridge

3.1 機制原理

很明顯:WebViewJavascriptBridge.js.txt主要用於銜接UIWebView中的web page,而WebViewJavascriptBridge.h/m則主要用於與ObjC的nativecode打交道。他們作為一個整體,其實起到了一個“橋樑”的作用,這三個檔案封裝了他們具體的互動處理方式,只開放出一些對外的涉及到業務處理的API,因此你在需要UIWebView與Nativecode互動的時候,引入該庫,則無需考慮太多的互動上的問題。整個的Bridge對你來說都是透明的,你感覺程式設計的時候,就像是web程式設計的前端和後端一樣清晰。

3.1.1 Native端工作機制
Native端中主要工作機制是,將當前WebView的Delegate截留,用當前Bridge例項物件作為委託Target物件,獲取到WebViewDelegate回撥方法(主要在shouldStartLoadWithRequest回撥方法中進行橋接處理)並加以處理後,再往上回調到目標VC中;具體程式碼如下所示:

- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(id)webViewDelegate handler:(WVJBHandler)messageHandler resourceBundle:(NSBundle*)bundle{

_messageHandler = messageHandler;

_webView = webView;

_webViewDelegate = webViewDelegate;

_messageHandlers = [NSMutableDictionary dictionary];

_webView.delegate = self;

_resourceBundle = bundle;

}

回撥上層方法示例:

__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;

if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {

[strongDelegate webViewDidFinishLoad:webView];

}

3.1.2 Native端事件解析處理機制
Js端需要傳遞的業務資料並不通過Url引數傳遞,而是通過在Native端呼叫js方法直接獲取資料JSON物件,再將其做解析處理。集中在- (void)_flushMessageQueue方法中處理。核心程式碼如下:

//1、獲取業務資料JSON物件字串

NSString *messageQueueString = [_webView stringByEvaluatingJavaScriptFromString:@WebViewJavascriptBridge._fetchQueue();];

//2、將物件序列化為陣列

id messages = [self _deserializeMessageJSON:messageQueueString];

//3、列舉每個Message物件並作解析處理

NSString* responseId = message[@responseId];

if (responseId) {

WVJBResponseCallback responseCallback = _responseCallbacks [responseId];

responseCallback(message[@responseData]);

[_responseCallbacks removeObjectForKey:responseId];

} else {

WVJBResponseCallback responseCallback = NULL;

NSString* callbackId = message[@callbackId];

if (callbackId) {

responseCallback = ^(id responseData) {

if (responseData == nil) {

responseData = [NSNull null];

}



WVJBMessage* msg = @{ @responseId:callbackId, @responseData:responseData };

[self _queueMessage:msg];

};

} else {

responseCallback = ^(id ignoreResponseData) {

// Do nothing

};

}



WVJBHandler handler;

if (message[@handlerName]) {

handler = _messageHandlers[message[@handlerName]];

} else {

handler = _messageHandler;

}

///!!!在此完成註冊事件回撥,包括註冊事件與接收資料事件

handler(message[@data], responseCallback);

}

3.1.3 js端工作機制
js端通過iFrame來觸發一次load動作,但是iFrame本身的Url並不攜帶資料,而僅僅是用於觸發load動作,具體的業務資料快取在sendMessageQueue陣列中。

Native端捕獲到load動作後,再呼叫window.WebViewJavascriptBridge類的_fetchQueue()方法獲取業務資料去做解析。

所以,js端作用主要有三個:

快取業務資料(包括註冊事件);提供公用方法供Native端呼叫,一般用於獲取快取的業務資料;觸發load動作以喚醒與Native的互動動作;

主要公用方法:

handleMessageFromObjC方法

用於供Native端傳送資料到js端;

fetchQueue方法:

用於供Native端獲取業務資料物件;

registerHandler(handlerName, handler)方法

用於註冊js事件方法,用於供Native端呼叫;

callHandler(handlerName, data, responseCallback)方法

用於呼叫Native端事件方法。

3.2 功能特性

【1】UI端

(1)UI端在初始化時支援設定訊息的預設處理器(這裡的訊息指的是從Native端接收到的訊息);

(2)從UI端向Native端傳送訊息,並支援對於Native端響應後的回撥處理的定義;

(3)UI端呼叫Native定義的處理器,並支援Native端響應後的回撥處理定義;

(4)UI端註冊處理器(供Native端呼叫),並支援給Native端響應處理邏輯的定義;

【2】Native端

(1)Native端在初始化時支援設定訊息的預設處理器(這裡的訊息指的是從UI端傳送過來的訊息);

(2)從Native端向UI端傳送訊息,並支援對於UI端響應後的回撥處理邏輯的定義;

(3)Native端呼叫UI端定義的處理器,並支援UI端給出響應後在Native端的回撥處理邏輯的定義;

(4)Native端註冊處理器(供UI端呼叫),並支援給UI端響應處理邏輯的定義UI端以及Native端完全是對等的兩端,實現也是對等的。一段是訊息的傳送端,另一段就是接收端。這裡為引起混淆,需要解釋一下我這裡使用的“響應”、“回撥”在這個上下文中的定義:

(1)響應:接收端給予傳送端的應答

(2)回撥:傳送端收到接收端的應答之後在接收端呼叫的處理邏輯

3.3 使用過程

1.初始化一個webview(viewdidload)

UIWebView* webView = [[UIWebView alloc] initWithFrame:self.view.bounds];

[self.view addSubview:webView];

2.將此webview與WebViewJavascriptBridge關聯(viewdidload)

if (_bridge) { return; }

[WebViewJavascriptBridge enableLogging];

_bridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) {

NSLog(@ObjC received message from JS: %@, data);

responseCallback(@Response for message from ObjC);

}];

ps:此時你的webview就與js搭上橋了。下面就是方法的互調和引數的互傳。

(1) js調oc方法(可以通過data給oc方法傳值,使用responseCallback將值再返回給js)

[_bridge registerHandler:@testObjcCallback handler:^(id data, WVJBResponseCallback responseCallback) {

NSLog(@testObjcCallback called: %@, data);

responseCallback(@Response from testObjcCallback);

}];

這裡注意testObjcCallback這個方法的標示。html那邊的命名要跟ios這邊相同,才能調到這個方法。當然這個名字可以兩邊商量著自定義。簡單明確即可。

(2)oc調js方法(通過data可以傳值,通過 response可以接受js那邊的返回值 )

id data = @{ @greetingFromObjC: @Hi there, JS! };

[_bridge callHandler:@testJavascriptHandler data:data responseCallback:^(id response) {

NSLog(@testJavascriptHandler responded: %@, response);

}];

注意這裡的 testJavascriptHandler也是個方法標示。

(3)oc給js傳值(通過 response接受返回值 )

[_bridge send:@A string sent from ObjC to JS responseCallback:^(id response) {

NSLog(@sendMessage got response: %@, response);

}];

(4)oc給js傳值(無返回值)

[_bridge send:@A string sent from ObjC after Webview has loaded.];

3.4 JS端使用

3.4.1 初始化示例程式碼

function connectWebViewJavascriptBridge(callback) {

if (window.WebViewJavascriptBridge) {

callback(WebViewJavascriptBridge)

} else {

//以事件監聽器形式註釋初始化方法

document.addEventListener('WebViewJavascriptBridgeReady', function() {

callback(WebViewJavascriptBridge)

}, false)

}

}



connectWebViewJavascriptBridge(function(bridge) {

var uniqueId = 1

function log(message, data) {

var log = document.getElementById('log')

var el = document.createElement('div')

el.className = 'logLine'

el.innerHTML = uniqueId++ + '. ' + message + ':
' + JSON.stringify(data)

if (log.children.length) { log.insertBefore(el, log.children[0]) }

else { log.appendChild(el) }

}

//呼叫物件Init初始化方法完成Bridge物件自身的初始化動作

bridge.init(function(message, responseCallback) {

log('JS got a message', message)

var data = { 'Javascript Responds':'Wee!' }

log('JS responding with', data)

responseCallback(data)

})

//註冊一些事件,供Native端呼叫

bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {

log('ObjC called testJavascriptHandler with', data)

var responseData = { 'Javascript Says':'Right back atcha!' }

log('JS responding with', responseData)

responseCallback(responseData)

})

})

3.4.2 註冊事件供Native呼叫
//註冊一些事件,供Native端呼叫

bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {

log('ObjC called testJavascriptHandler with', data)

var responseData = { 'Javascript Says':'Right back atcha!' }

log('JS responding with', responseData)

responseCallback(responseData)

})

3.4.3 UI事件傳送資料到Native

var button = document.getElementById('buttons').appendChild(document.createElement('button'))

button.innerHTML = 'Send message to ObjC'

button.onclick = function(e) {

e.preventDefault()

var data = 'Hello from JS button'

log('JS sending message', data)

bridge.send(data, function(responseData) {

log('JS got response', responseData)

})

}

document.body.appendChild(document.createElement('br'))

3.4.4 UI事件呼叫Native註冊方法

var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))

callbackButton.innerHTML = 'Fire testObjcCallback'

callbackButton.onclick = function(e) {

e.preventDefault()

log('JS calling handler testObjcCallback')

bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {

log('JS got response', response)

})

}

3.5 Native端使用

3.5.1 初始化
建議在ViewDidLoad中完成

_bridge = [WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) {

NSLog(@ObjC received message from JS: %@, data);

responseCallback(@Response for message from ObjC);

}];

3.5.2 註冊事件方法供JS呼叫

[_bridge registerHandler:@testObjcCallback handler:^(id data, WVJBResponseCallback responseCallback) {

NSLog(@testObjcCallback called: %@, data);

responseCallback(@Response from testObjcCallback);

}];

3.5.3 傳送資料到JS端

[_bridge send:@A string sent from ObjC before Webview has loaded. responseCallback:^(id responseData) {

NSLog(@objc got response! %@, responseData);

}];

3.5.4 呼叫JS中方法

[_bridge callHandler:@testJavascriptHandler data: @{ @foo:@before ready }];

4 原生+h5頁面的APP業務解決方案設想

4.1 js端呼叫APP方法

WebViewJavascriptBridge支援主動傳送資料與呼叫Native已註冊事件方法來與Native端互動。

兩種機制各有所長,不過為了保證向後相容性,建議主要採用主動傳送資料的方法來實現與Native端的互動。儘量將UI端的業務抽象、歸類,以命令碼+子命令碼+引數資料的形式,封裝到JSON物件中,傳遞到Native端,Native端的解析邏輯統一採用一個引擎類來集中處理。

這樣也利於IOS與Android兩個平臺的邏輯統一。

以整合微信支付為例:

因為支付SDK只能用原生端介面,所以可以考慮將各種js端支付操作(發起微信支付、領取卡券等)抽象化成不同型別的命令,使用者點選頁面“支付”按鈕時,h5頁面傳送支付命令到Native端,Native將命令進行解析後,調起微信SDK中支付介面完成支付,獲取到支付結果後再將結果回撥給h5頁面。

因為這類APP是以資訊展示為主的APP,各類商品展示頁面肯定會繼續使用h5頁面,但是涉及到與Native端互動時,就需要一個統一的橋接層來處理各類業務操作,就不要使用各種封裝具體某類Native端功能的第三方庫了,總的設計思想應該是設計一個統一的橋接層,然後基於此來統一封裝Native端的各種能力,這一方面,微信是這麼做的、美居也是這麼做的。

4.2 APP端呼叫js方法

APP端呼叫js方法,建議也採用同樣策略,制定一個相互呼叫規範,明確命令編碼,採用json物件傳遞物件資料。不過App端可提供部分註冊方法,供js端來獲取APP端通用資訊,例如獲取裝置資訊、尺寸螢幕的。

二、隨著微信的不斷推廣,在移動開發的過程中原生與H5的混合開發在移動開發中佔據著越來越重要的地位;下面我給大家分享一下我在混合開發中的心得!

將HTML頁面載入到UIWebview控制元件中:

A:本地載入:

NSString *webPath = [[NSBundlemainBundle]pathForResource:@“HelloWord"ofType:@"html”];//獲取檔案路徑
NSURL *webURL = [NSURLfileURLWithPath:webPath];//通過檔案路徑字串設定URL
NSURLRequest *URLRequest = [NSURLRequestrequestWithURL:webURL];//設定請求提交的相關URL
[self.webView loadRequest:URLRequest];//提交請求

B:從伺服器載入HTML頁面

NSString *urlStr = [NSString stringWithFormat:@"%@/appservice/getAnnouncementDetail.do?id=%@",O2O_URL,self.idStr];
NSURL *url = [NSURL URLWithString:urlStr];
NSLog(@"url:%@",url);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];

2.加入UIWebViewDelegate委託(如果想要進行相應的資料互動必須新增相應的我委託)

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType//html頁面的js動作觸發時執行

-(void)webViewDidFinishLoad:(UIWebView *)webView //html載入完後觸發

3.OC提交JS請求.執行JS方法

[self.webViewstringByEvaluatingJavaScriptFromString:@”方法名(%@)",引數]];

4.JS提交請求到OC

-(BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType{
NSString *requestString = [[requestURL]absoluteString];//獲取請求的絕對路徑.
NSArray *components = [requestStringcomponentsSeparatedByString:@":”];//提交請求時候分割引數的分隔符
if ([componentscount] >1 && [(NSString *)[componentsobjectAtIndex:0]isEqualToString:@"testapp"]) {
//過濾請求是否是我們需要的.不需要的請求不進入條件
if([(NSString *)[components objectAtIndex:1]isEqualToString:@"change"])
{
//所要執行的操作
}
returnNO;
}
returnYES;
}

5.在HTML5中JS的寫法

1.接收請求的方法寫法

function
fund(){
//操作
}

2.提交請求的方法寫法

function sendCommand(cmd,param){
var url="testapp:"+cmd+":"+param;//組合請求字串
document.location = url;//提交的請求
}