簡介
UIWebView 可以在你的應用程序嵌入網頁內容并與之交互肴捉,但它笨重難用還有內存泄漏且。
WKWebView 代替了UIKit 中的 UIWebView 和 AppKit 中的 WebView,提供了統一的跨雙平臺 API珊蟀。它擁有高達60fps的滾動刷新率以及內置手勢忱辅,在性能死嗦、穩(wěn)定性举哟、功能方面有很大提升俯渤,支持了更多的HTML5特性,和 Safari 相同的 JavaScript 引擎侨嘀。
1. UIWebView
1.1 加載方法
//創(chuàng)建UIWebView
UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
webView.delegate = self;
[self.view addSubview:webView];
//加載URL
NSURL *url = [[NSURL alloc] initWithString:@"http://www.biadu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
加載方法有3種
- (void)loadRequest:(NSURLRequest *)request; //直接裝載URL
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;//加載HTML代碼
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;//用于轉載本地頁面或者外部傳來的NSData
1.2 代理回調
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;//進行加載前的預判斷臭挽,如果返回YES,則會進入后續(xù)流程
- (void)webViewDidStartLoad:(UIWebView *)webView;//開始加載網頁
- (void)webViewDidFinishLoad:(UIWebView *)webView;//加載完成
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;//加載失敗
1.3 Native與JS的相互調用
1.3.1 Native調用JS中的方法
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
//使用方法 JSFunctionName為JS內方法名稱 paramete是參數
[self. webView stringByEvaluatingJavaScriptFromString:@"JSFunctionName('paramete')"]
1.3.2 JS調用Native中的方法
JS調用Native方法會在其他線程中調用咬腕,所以更新UI則需要放在主線程中進行
#import <JavaScriptCore/JavaScriptCore.h>
可以給類添加JSExport協議欢峰,
凡是添加了JSExport協議類我們就可以通過JS調用到其方法變量等。
//此宏定義用于js調用oc方法
#define JSExportAs(PropertyName, Selector) \
@optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector
#endif
例如如下:在js中調用doFoo 即調用了本類的- (void)doFoo:(id)foo 方法
@textblock
@protocol MyClassJavaScriptMethods <JSExport>
JSExportAs(doFoo,
- (NSString *)doFoo:(NSString *)foo
);
@end
@/textblock
//OC中的實現都需要加入 #import <JavaScriptCore/JavaScriptCore.h>
@property(nonatomic,strong)JSContext *jsContext;
//配置JSContext
-(void)configJsContext{
if (_jsContext) return;
_jsContext = [[self contentWebview] valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
JSExportObject *jsExportObject = [[JSExportObject alloc] init];
jsExportObject.delegate = self;
//把實現JSExport協議的對象添加到JSContext中的native對象doFoo方法 它會自動指定到 -(NSString *)doFoo:(id)foo這個方法當中去 且doFoo可以有NSString返回值
_jsContext[@"native"] = jsExportObject;
}
//在OC中已經添加了native 對象涨共,可以在js中調用native 定義的
var s= {
methodId: i,
params: p
};
native. doFoo(JSON.stringify(s))
2. WKWebView
2.1 加載方法
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
YFMWKWebView *wkWebView = [[YFMWKWebView alloc] initWithFrame:self.view.bounds configuration:config];
wkWebView.navigationDelegate = self;
wkWebView.UIDelegate = self;
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.biadu.com"]];
[wkWebView loadRequest:mutableRequest];
加載方法有4種纽帖, 基本與UIWebView相同只是多了一項本地文件加載
//iOS 8
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
//iOS 9
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));
2.2 代理回調
3種代理類型
WKNavigationDelegate( 提供了追蹤主窗口網頁加載過程和是否進行頁面加載新頁面的代理方法)
追蹤主窗口網頁加載過程
// 頁面開始加載時調用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 當內容開始返回時調用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 頁面加載完成之后調用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 頁面加載失敗時調用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
是否進行頁面加載新頁面
// 接收到服務器跳轉請求之后調用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
}
// 在收到響應后,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
decisionHandler(WKNavigationResponsePolicyAllow);
}
// 在發(fā)送請求之前举反,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
decisionHandler(WKNavigationActionPolicyAllow);
}
WKUIDelegate(用原生控件顯示網頁的方法回調)
主要用于WKWebView處理web界面的三種提示框(警告框懊直、確認框、輸入框)
//警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
//確認框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
//輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;
WKScriptMessageHandler(提供從網頁中收消息的回調方法)
OC中接收JS的調用信息
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"JS 調用了 %@ 方法火鼻,傳回參數 %@",message.name, message.body);
}
2.3 Native與JS的相互調用
2.3.1 Native調用JS中的方法
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
//使用方法 JSFunctionName為JS內方法名稱 paramete是參數
[wkWebView evaluateJavaScript:@"JSFunctionName(‘paramete’)" completionHandler:^(id item, NSError * error) {
}];
2.3.2 JS調用Native中的方法
WKWebView中JS調用Native方法會在主線程中進行
如要進行JS調用Native需要進行WKWebView的配置 方法如下:
Native中需要添加配置信息室囊,最后把配置好的config添加到列表2.1
中。
//創(chuàng)建配置
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//添加消息處理
WKUserContentController *controller = wkWebView.configuration.userContentController;
[controller addScriptMessageHandler:self name:@"callMe"];
Native向Web中注入自定義的JS代碼
在WKUserScript中添加自定義JS代碼魁索,然后提交到configuration.userContentController中
//把[configuration.userContentController addUserScript:self.script];添加到列表2.1中
- (WKUserScript *)script {
if (!_script) {
NSString *jsString = @"function JSFunctionName(a) {\
var i = {\
params: a\
};\
return window.webkit.messageHandlers.callMe.postMessage(i);\
}";
// 根據JS字符串初始化WKUserScript對象
WKUserScript *script = [[WKUserScript alloc] initWithSource:jsString
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
forMainFrameOnly:YES];
_script = script;
}
return _script;
}
JS中添加如下方法進行調用
//此方法內的<name>可以寫成ScriptMessageHandler中定義的name融撞,<messageBody>為要傳的信息內容
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
3. 問題解決
一 . 在使用WKWebView時如果你在dealloc打個斷點,如果發(fā)現沒有被釋放有可能是引起了循環(huán)引用問題(發(fā)生在addScriptMessageHandler方法中)
解決方法:
1.自定義一個WKScriptMessageHandler 采用weak方法實現代理方法
2.在離開頁面時添加removeScriptMessageHandlerForName這個方法進行移除操作
二. WKWebView JS調用OC 獲取返回值問題 因為- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message 此方法沒有返回值所以不能實現返回值回調粗蔚。
方法一: 需要借用到OC調用JS方法邏輯重新調用JS中Callback方法來實現回調 參考本文2.3
或者使用
方法二:可以利用runJavaScriptTextInputPanelWithPrompt回調來實現尝偎。
首先注入如下代碼,主要實現所有調用OC方法都是通過prompt來實現
//把[configuration.userContentController addUserScript:self.script];添加到列表2.1中
- (WKUserScript *)script {
if (!_script) {
NSString *jsString = @"function JSFunctionName(a) {\
var i = {\
params: a\
};\
return prompt('Native_'+i);\
}";
// 根據JS字符串初始化WKUserScript對象
WKUserScript *script = [[WKUserScript alloc] initWithSource:jsString
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
forMainFrameOnly:YES];
_script = script;
}
return _script;
}
其次在OC中截獲Prompt并處理后返回數據
-(void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
//如果是調用原生
if (prompt && [prompt hasPrefix:@"Native_"]) {
completionHandler(此處填寫處理內容);
}else{
//此處填寫自定義promot
}
}```