目前,大多數的應用中都嵌入了H5。那么在iOS應用中如何嵌入一個H5,並且和它進行交互就成了
一個勢必要掌握的技術了。本文我將結合我在項目中的一些需求整理出對應的技術點,僅供參考。
在iOS開發中,H5的嵌入可以通過UIWebView或者WKWebView。這兩個都是繼承UIView,來加載web數據的類。
UIWebView是在iOS2的時候開始使用的。特點是加載速度慢,占用內存多,優化艱難。WKWebView是在iOS8蘋果
新推出的,加載速度快,占用內存較少,是一個不錯的選擇。如果想要比較兩者的區別,您可以選擇一個網頁進行
測試一下。鑒上所述,我們選擇WKWebView進行開發。
- WKWebView的創建
- (void)createWebView{ WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; // 根據需要去設置對應的屬性 WKWebView *webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:config]; webView.navigationDelegate = self; [self.view addSubview:webView]; NSURL *url = [NSURL URLWithString:self.strURL]; [self loadWebViewWithURL:url]; // JS調用OC 添加處理腳本 [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"Share"]; }
- JS調用OC代碼
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"Share"];
這是利用WKWebView的一個新特性MessageHandler來處理JS調用原生方法。要實現JS調用iOS原生方法,步驟見下。
-
添加<WKScriptMessageHandler>協議。讓控制器成為MessageHandler的代理對象。
-
對於監聽的方法名要和JS開發的人商量好。這里我們監聽的是Share方法,對於JS開發的人員必須要以以下方式寫。
window.webkit.messageHandlers. Share.postMessage(null)
-
實現協議方法。在這個方法里message參數有一個屬性body。message.body就是JS傳過來的參數,可以是字符串,可以是數組,也可以是字典。通過message.name判斷可以知道監聽的是JS的哪個方法。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { if ([message.name isEqualToString:@"Share"]) { //TODO } }
至此,JS調用OC代碼就已完結。是不是很簡單。另外,我在網上也看到了不一樣的處理方式。大家可以參考WebViewJavascriptBridge我覺得寫的比較清楚。
- OC調用JS代碼
[self.webView evaluateJavaScript:@"show()" completionHandler:^(id _Nullable response, NSError * _Nullable error) { //TODO }];
相信代碼已經看得很清楚啦。show()就是JS寫的方法,這個方法可傳可不傳參數,具體依實際情況而定。另外關於UIWebView和JS的交互,以下部分僅供參考。
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; context[@"Share"] = ^() { NSArray *args = [JSContext currentArguments]; dispatch_async(dispatch_get_main_queue(), ^{ //TODO });
- 關於<WKNavigationDelegate>
網頁加載開始,結束,失敗這幾個都特別簡單,我就不贅述了。說一下下面這個協議方法,這個方法發生在頁面跳轉中。WKNavigationActionPolicy是一個枚舉,
WKNavigationActionPolicyAllow表示允許跳轉,WKNavigationActionPolicyCancel表示取消跳轉。對了,這里還有一個補充: scrollView嵌套網頁和原生view,原生view要根據網頁的高度來布局。
我看到不少的電商應用都有這種布局,但在算高度上會有各種問題,不知道你們有遇見過?
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSString *url = navigationAction.request.URL.absoluteString; if(![url isEqualToString:self.strURL]) { // 頁面跳轉 } decisionHandler(WKNavigationActionPolicyAllow); }
- 關於< WKUIDelegate >
不知道您有沒有遇見過JS寫的alert()框在iOS上不彈出。那么您有沒有實現這些協議方法呢。
/// 創建一個新的WebView - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures; /// 輸入框 - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler; /// 確認框 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler; /// 警告框 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
- 獲取網頁標題,網頁加載進度和加載狀態
這是通過KVO的方式進行監聽的。您可以點擊進WKWebView的內部看一下,他們每個屬性上面都有很長的解釋,你不難發現這一段。舉一個獲取標題的例子。
其他的類似。別忘了,KVO監聽在dealloc中移除監聽者哦。
[self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"title"]) { if (object == self.webView) { if(self.navigationController) self.navigationItem.title = self.webView.title; } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } }