簡(jiǎn)單說兩句护昧,混合開發(fā)的App(Hybrid App)就是在一個(gè)App中內(nèi)嵌一個(gè)輕量級(jí)的瀏覽器魂迄,一部分原生的功能改為Html 5來開發(fā),這部分功能不僅能夠在不升級(jí)App的情況下動(dòng)態(tài)更新惋耙,而且可以在Android或iOS的App上同時(shí)運(yùn)行捣炬,讓用戶的體驗(yàn)更好又可以節(jié)省開發(fā)的資源。
混合開發(fā)又它的優(yōu)勢(shì)绽榛,也有其劣勢(shì)湿酸。所以并不是所有的app都適合混合開發(fā),對(duì)于一些強(qiáng)交互的app用h5來做的話灭美,會(huì)事倍功半推溃,而且體驗(yàn)上面也會(huì)大打折扣。所以届腐,公司在選擇開發(fā)模式的時(shí)候铁坎,一定要選擇適合自己產(chǎn)品的,而不是一味的跟風(fēng)
關(guān)于第三發(fā)框架有很多犁苏,這里就不一一列舉(我知道的也不多)硬萍,在這里我介紹一下交互的底層原理,對(duì)于iOS主要分為(UIWebView-JS围详、WKWebView-JS)朴乖,兩者的原理有所不同,但是都需要了解的
UIWebView 與JS交互
OC調(diào)用JS
這里蘋果公司已經(jīng)為我們封裝好了一個(gè)方法
其中script里面放的就是純JS代碼短曾,而我們只需要用UIWebView的
stringByEvaluatingJavaScriptFromString:(NSString *)script
對(duì)象方法就可以直接執(zhí)行字符串所存儲(chǔ)的JS代碼寒砖,當(dāng)然,這個(gè)執(zhí)行嫉拐,是在整個(gè)頁(yè)面加載完以后才會(huì)去執(zhí)行的哩都。
NSString *js = @"var div = document.getElementById('div1'); div.style.backgroundColor = 'yellow';div.addEventListener('click',divClick);function divClick(){alert('123456789')}";
/*加載js腳本*/
[_webView stringByEvaluatingJavaScriptFromString:js];
JS調(diào)用OC
這需要UIWebView的一個(gè)代理方法來輔助操作,當(dāng)JS需要調(diào)用OC 的一個(gè)方法的時(shí)候會(huì)主動(dòng)發(fā)起一個(gè)網(wǎng)絡(luò)請(qǐng)求
window.location.href = "openimagepicker://"
只要JS掉用和href這個(gè)方法婉徘,就會(huì)走UIWebView 的代理方法:
(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
并且將 "openimagepicker://"當(dāng)做request 傳進(jìn)來漠嵌,也就是說咐汞,我們需要在這個(gè)代理方法里來攔截這個(gè)request,看是否是OC來處理的儒鹿,比如說我當(dāng)前發(fā)送的request的意思是"打開相冊(cè)"(事先約定好)化撕,我通過webview 的代理方法攔截到了這個(gè)請(qǐng)求,那么我就會(huì)主動(dòng)的掉起系統(tǒng)原生相冊(cè)约炎,代碼如下:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(@"%@",request.URL.absoluteString);
//獲取webView發(fā)送的請(qǐng)求
NSString *urlString = request.URL.absoluteString;
/**
* 打開相冊(cè)
*/
if ([urlString isEqualToString:@"openimagepicker://"])
{
[self openImagePicker];
return NO;
}
return YES;
}
- (void)openImagePicker
{
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
[self presentViewController:imagePickerController animated:YES completion:nil];
}
return NO是為了不讓webview 來處理這次網(wǎng)絡(luò)請(qǐng)求植阴,因?yàn)檫@并不是一次真正的網(wǎng)絡(luò)請(qǐng)求
WKWebView與JS交互
JS調(diào)用OC
相對(duì)于UIWebView,WKWebView與JS進(jìn)行交互時(shí)則要麻煩很多圾浅,大體上分為3個(gè)步驟:
1.首先我們?cè)诔跏蓟疻KWebView的時(shí)候 需要將一個(gè)配置項(xiàng)同事給webView掠手,這里面的配置項(xiàng),使我們來注冊(cè)handler的(一會(huì)再解釋這是干啥的)
2.注冊(cè)完后狸捕,網(wǎng)頁(yè)跑了起來喷鸽,JS需要通過window.webkit.messageHandlers.<name>.postMessage(<messageBody>)調(diào)用的觸發(fā)webView的代理方法
3.Native(原生)通過代理方法,來處理響應(yīng)的事件
需要的類(OC)
WKWebViewConfiguration 這個(gè)類就是webView初始化時(shí)候的配置項(xiàng)灸拍,它會(huì)有一個(gè)方法做祝,讓我們來注冊(cè)handler
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
這個(gè)方法就是來注冊(cè)handler用的
完整代碼塊
NSString *path = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
//2.讀取文件中的內(nèi)容
NSString *string = [NSString stringWithContentsOfURL:[NSURL fileURLWithPath:path] encoding:NSUTF8StringEncoding error:nil];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
//注冊(cè),方便js調(diào)用oc代碼
[configuration.userContentController addScriptMessageHandler:self name:@"OpenImagePicker"];
[configuration.userContentController addScriptMessageHandler:self name:@"SystemVersion"];
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
//加載指定的HTML字符串
[webView loadHTMLString:string baseURL:nil];
webView.UIDelegate = self;
[self.view addSubview:webView];
假如JS主動(dòng)調(diào)起了OC鸡岗,webView 的這個(gè)代理方法會(huì)立即執(zhí)行,我們可以在這里處理JS需要Native做的事情
//JS通過window.webkit.messageHandlers.<name>.postMessage(<messageBody>)調(diào)用的觸發(fā)此代理方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
//打開相冊(cè)
if ([message.name isEqualToString:@"OpenImagePicker"])
{
//js的傳過來的數(shù)據(jù)
NSLog(@"%@",message.body);
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
[self presentViewController:picker animated:YES completion:nil];
}
//獲取系統(tǒng)版本
if ([message.name isEqualToString:@"SystemVersion"])
{...}
}
那么問題來了混槐?JS怎么來主動(dòng)掉起Native 來這行上面這個(gè)代理方法呢,還記得剛才注冊(cè)的handler么纤房?那是注冊(cè)在webView上的纵隔,就相當(dāng)于OC跟JS事先商量好,有哪些字段會(huì)調(diào)起我的代理方法炮姨,其他字段是不可以的捌刮!
JS 的代碼如下:
window.webkit.messageHandlers.OpenImagePicker.postMessage("123456789")
其中,"OpenImagePicker"并不是JS 的方法名舒岸,而是我們之前在webView里注冊(cè) 的handler绅作,"postMessage"是JS帶給OC 的一些信息,如果"OpenImagePicker"我們事先沒有注冊(cè)到webView里蛾派,當(dāng)JS執(zhí)行這句代碼時(shí)俄认,是不會(huì)走webView 的代理方法的。
OC調(diào)用JS
OC調(diào)用JS相對(duì)要簡(jiǎn)單一些洪乍,有兩種眯杏,其中一種跟UIWebView 的方法一樣,都是系統(tǒng)自帶的方法壳澳,下面貼代碼
[webView evaluateJavaScript:@"function sum(a,b){return a + b};sum(1,2)" completionHandler:^(id result, NSError * _Nullable error) {
NSLog(@"%@",result);
}];
自己去看log出來的結(jié)果是啥
還有第二種岂贩,就是在webView初始化的時(shí)候,直接注入JS代碼巷波,放在JS代碼的最后面執(zhí)行萎津,貼代碼
//初始化了script
WKUserScript *script = [[WKUserScript alloc] initWithSource:@"buttonClick()" injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
//執(zhí)行js語(yǔ)言的
[configuration.userContentController addUserScript:script];
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
//加載指定的HTML字符串
[webView loadHTMLString:string baseURL:nil];
webView.UIDelegate = self;
//加載指定路徑下的文件
[self.view addSubview:webView];
多余的代碼我沒貼卸伞,這里還是很容易懂的。
總結(jié):?jiǎn)魡暨@么多锉屈,就是讓大家知道荤傲,我是誰?我在哪颈渊?在干嗎遂黍?原理還是要略知一二的,大多數(shù)用的都是別人的框架俊嗽,用的時(shí)候很少考慮原理妓湘,當(dāng)業(yè)務(wù)需求有變化的時(shí)候,需要改底層框架乌询!就會(huì)感嘆一句:臥槽!我是誰豌研?我在哪妹田?在干嗎?
阿里嘎豆鹃共!