apple官方文檔https://developer.apple.com/documentation/webkit
WKWebView 是蘋果在iOS 8中引入的新組件父叙,目的是提供一個(gè)現(xiàn)代的支持最新Webkit功能的網(wǎng)頁(yè)瀏覽控件趾唱,擺脫過(guò)去 UIWebView的老蜻懦、舊、笨悠咱,特別是內(nèi)存占用量巨大的問(wèn)題析既。它使用與Safari中一樣的Nitro JavaScript引擎谆奥,大大提高了頁(yè)面js執(zhí)行速度。
相比于UIWebView的優(yōu)勢(shì):?
在性能空骚、穩(wěn)定性擂仍、占用內(nèi)存方面有很大提升逢渔;?
允許JavaScript的Nitro庫(kù)加載并使用(UIWebView中限制)?
增加加載進(jìn)度屬性:estimatedProgress肃廓,不用在自己寫假進(jìn)度條了?支持了更多的HTML的屬性
具體分析WKWebView的優(yōu)劣勢(shì)
內(nèi)存占用是UIWebView的1/4~1/3
頁(yè)面加載速度有提升诲泌,有的文章說(shuō)它的加載速度比UIWebView提升了一倍左右。
更為細(xì)致地拆分了 UIWebViewDelegate 中的方法
自帶進(jìn)度條哀蘑。不需要像UIWebView一樣自己做假進(jìn)度條(通過(guò)NJKWebViewProgress和雙層代理技術(shù)實(shí)現(xiàn))绘迁,技術(shù)復(fù)雜度和代碼量,根貼近實(shí)際加載進(jìn)度優(yōu)化好的多棠赛。
允許JavaScript的Nitro庫(kù)加載并使用(UIWebView中限制)
可以和js直接互調(diào)函數(shù)睛约,不像UIWebView需要第三方庫(kù)WebViewJavascriptBridge來(lái)協(xié)助處理和js的交互哲身。
不支持頁(yè)面緩存,需要自己注入cookie,而UIWebView是自動(dòng)注入cookie膀值。
無(wú)法發(fā)送POST參數(shù)問(wèn)題
基本參數(shù)解釋:
WKWebView:網(wǎng)頁(yè)的渲染與展示沧踏,通過(guò)WKWebViewConfiguration可以進(jìn)行自定義配置巾钉。
WKWebViewConfiguration:這個(gè)類專門用來(lái)配置WKWebView。
WKPreference:這個(gè)類用來(lái)進(jìn)行相關(guān)webView設(shè)置潦匈。
WKProcessPool:這個(gè)類用來(lái)配置進(jìn)程池赚导,與網(wǎng)頁(yè)視圖的資源共享有關(guān)吼旧。
WKUserContentController:這個(gè)類主要用來(lái)做native與JavaScript的交互管理。
WKUserScript:用于進(jìn)行JavaScript注入掂为。
WKScriptMessageHandler:這個(gè)類專門用來(lái)處理JavaScript調(diào)用native的方法员串。
WKNavigationDelegate:網(wǎng)頁(yè)跳轉(zhuǎn)間的導(dǎo)航管理協(xié)議寸齐,這個(gè)協(xié)議可以監(jiān)聽(tīng)網(wǎng)頁(yè)的活動(dòng)抄谐。
WKNavigationAction:網(wǎng)頁(yè)某個(gè)活動(dòng)的示例化對(duì)象斯稳。
WKUIDelegate:用于交互處理JavaScript中的一些彈出框迹恐。
WKBackForwardList:堆棧管理的網(wǎng)頁(yè)列表殴边。
WKBackForwardListItem:每個(gè)網(wǎng)頁(yè)節(jié)點(diǎn)對(duì)象。
1.加載網(wǎng)頁(yè)
WKWebViewConfiguration *webConfiguration = [WKWebViewConfiguration new];
???WKWebView *webView=[[WKWebView alloc]initWithFrame:CGRectZero configuration:webConfiguration];
????[self.view addSubview:self.webView];
????[webView mas_makeConstraints:^(MASConstraintMaker *make) {
????????make.top.offset(self.zxNavigationbarHeight);
????????make.left.right.offset(0);
????????make.bottom.offset(0);
????}];
????NSURL *url = [NSURL URLWithString:self.strUrl];
????NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
????[webView loadRequest:request];
2.進(jìn)度條和標(biāo)題
????_progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, self.zxNavigationbarHeight,?????????????????????????????????????????????????????self.view.frame.size.width, 2)];
????_progressView.tintColor = [UIColor blueColor];
????_progressView.trackTintColor = [UIColor clearColor];
????[self.view addSubview:_progressView];
//添加監(jiān)測(cè)網(wǎng)頁(yè)加載進(jìn)度的觀察者
????[self.webView addObserver:self
???????????????????forKeyPath:NSStringFromSelector(@selector(estimatedProgress))
??????????????????????options:0
??????????????????????context:nil];
????[self.webView addObserver:self
???????????????????forKeyPath:@"title"
??????????????????????options:NSKeyValueObservingOptionNew
??????????????????????context:nil];
//kvo 監(jiān)聽(tīng)進(jìn)度和標(biāo)題 必須實(shí)現(xiàn)此方法
-(void)observeValueForKeyPath:(NSString *)keyPath
?????????????????????ofObject:(id)object
???????????????????????change:(NSDictionary<NSKeyValueChangeKey,id> *)change
??????????????????????context:(void *)context{
????if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))]
????????&& object == _webView) {
????????DDLog(@"網(wǎng)頁(yè)加載進(jìn)度 = %f",_webView.estimatedProgress);
????????self.progressView.progress = _webView.estimatedProgress;
????????if (_webView.estimatedProgress >= 1.0f) {
????????????dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
????????????????self.progressView.progress = 0;
????????????});
????????}
????}else if([keyPath isEqualToString:@"title"]
?????????????&& object == _webView){
????????self.navigationItem.title = _webView.title;
????}else{
????????[super observeValueForKeyPath:keyPath
?????????????????????????????ofObject:object
???????????????????????????????change:change
??????????????????????????????context:context];
????}
}
- (void)dealloc{
????//移除觀察者
????[_webView removeObserver:self
??????????????????forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
????[_webView removeObserver:self
??????????????????forKeyPath:NSStringFromSelector(@selector(title))];
}
3.刷新、上一頁(yè)拳氢、下一頁(yè)
- (void)goBackAction:(id)sender{
????if ([self.webView canGoBack]) {
????????[self.webView goBack];
????}
}
- (void)refreshAction:(id)sender{
????[self.webView reload];
}
-(void)goNextAction:(id)sender{
????if ([self.webView canGoForward]) {
????????[self.webView goForward];
????}
}
4.OC調(diào)用JS
//OC調(diào)用JS
- (void)ocToJs{
????//changeColor()是JS方法名馋评,completionHandler是異步回調(diào)block
????NSString *jsString = [NSString stringWithFormat:@"changeColor('%@')", @"Js顏色參數(shù)"];
????[_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) {
????????DDLog(@"改變HTML的背景色");
????}];
????//改變字體大小 調(diào)用原生JS方法
????NSString *jsFont = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'", arc4random()%99 + 100];
????[_webView evaluateJavaScript:jsFont completionHandler:nil];
????NSString * path =??[[NSBundle mainBundle] pathForResource:@"girl" ofType:@"png"];
????NSString *jsPicture = [NSString stringWithFormat:@"changePicture('%@','%@')", @"pictureId",path];
????[_webView evaluateJavaScript:jsPicture completionHandler:^(id _Nullable data, NSError * _Nullable error) {
????????DDLog(@"切換本地頭像");
????}];
}
//OC調(diào)用JS改變背景色
????function changeColor(parameter)
????{
????????document.body.style.backgroundColor = randomColor();
????}
5.JS調(diào)用OC
//方式一.WKNavigationDelegate通過(guò)即將跳轉(zhuǎn)的url攔截
// 根據(jù)WebView對(duì)于即將跳轉(zhuǎn)的HTTP請(qǐng)求頭信息和相關(guān)信息來(lái)決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
????NSString * urlStr = navigationAction.request.URL.absoluteString;
????NSLog(@"發(fā)送跳轉(zhuǎn)請(qǐng)求:%@",urlStr);
????//自己定義的協(xié)議頭
????NSString *htmlHeadString = @"github://";
????if([urlStr hasPrefix:htmlHeadString]){
????????UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"通過(guò)截取URL調(diào)用OC" message:@"你想前往我的Github主頁(yè)?" preferredStyle:UIAlertControllerStyleAlert];
????????[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
????????}])];
????????[alertController addAction:([UIAlertAction actionWithTitle:@"打開(kāi)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
????????????NSURL * url = [NSURL URLWithString:[urlStr stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]];
????????????[[UIApplication sharedApplication] openURL:url];
????????}])];
????????[self presentViewController:alertController animated:YES completion:nil];
????????decisionHandler(WKNavigationActionPolicyCancel);
????}else{
????????decisionHandler(WKNavigationActionPolicyAllow);
????}
}
//方式二.WKScriptMessageHandler注冊(cè)相應(yīng)交互事件
//自定義的WKScriptMessageHandler 是為了解決內(nèi)存不釋放的問(wèn)題
????????WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
????????//這個(gè)類主要用來(lái)做native與JavaScript的交互管理
????????WKUserContentController * wkUController = [[WKUserContentController alloc] init];
????????//注冊(cè)一個(gè)name為jsToOcNoPrams的js方法 設(shè)置處理接收J(rèn)S方法的對(duì)象
????????[wkUController addScriptMessageHandler:weakScriptMessageDelegate??name:@"jsToOcNoPrams"];
????????[wkUController addScriptMessageHandler:weakScriptMessageDelegate??name:@"jsToOcWithPrams"];
????????config.userContentController = wkUController;
- (void)dealloc{
????//移除注冊(cè)的js方法
????[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcNoPrams"];
????[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcWithPrams"];
}
//被自定義的WKScriptMessageHandler在回調(diào)方法里通過(guò)代理回調(diào)回來(lái),繞了一圈就是為了解決內(nèi)存不釋放的問(wèn)題
//通過(guò)接收J(rèn)S傳出消息的name進(jìn)行捕捉的回調(diào)方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
????DDLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
????//用message.body獲得JS傳出的參數(shù)體
????NSDictionary * parameter = message.body;
????//JS調(diào)用OC
????if([message.name isEqualToString:@"jsToOcNoPrams"]){
????????UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js調(diào)用到了oc" message:@"不帶參數(shù)" preferredStyle:UIAlertControllerStyleAlert];
????????[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
????????}])];
????????[self presentViewController:alertController animated:YES completion:nil];
????}else if([message.name isEqualToString:@"jsToOcWithPrams"]){
????????UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js調(diào)用到了oc" message:parameter[@"params"] preferredStyle:UIAlertControllerStyleAlert];
????????[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
????????}])];
????????[self presentViewController:alertController animated:YES completion:nil];
????}
}
function jsToOcFunction1()
????{
???????window.webkit.messageHandlers.jsToOcNoPrams.postMessage({});
????}
????function jsToOcFunction2()
????{
????????window.webkit.messageHandlers.jsToOcWithPrams.postMessage({"params":"我是參數(shù)"});
????}
//方式三.WKUIDelegate捕獲系統(tǒng)交互
/**
*??web界面中有彈出警告框時(shí)調(diào)用
*
*??@param webView??????????實(shí)現(xiàn)該代理的webview
*??@param message??????????警告框中的內(nèi)容
*??@param completionHandler 警告框消失調(diào)用
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
????UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"HTML的彈出框" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
????[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
????????completionHandler();
????}])];
????[self presentViewController:alertController animated:YES completion:nil];
}
// 確認(rèn)框
//JavaScript調(diào)用confirm方法后回調(diào)的方法 confirm是js中的確定框,需要在block中把用戶選擇的情況傳遞進(jìn)去
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
????UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
????[alertController addAction:([UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
????????completionHandler(NO);
????}])];
????[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
????????completionHandler(YES);
????}])];
????[self presentViewController:alertController animated:YES completion:nil];
}
// 輸入框
//JavaScript調(diào)用prompt方法后回調(diào)的方法 prompt是js中的輸入框 需要在block中把用戶輸入的信息傳入
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
????UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
????[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
????????textField.text = defaultText;
????}];
????[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
????????completionHandler(alertController.textFields[0].text?:@"");
????}])];
????[self presentViewController:alertController animated:YES completion:nil];
}
// 頁(yè)面是彈出窗口 _blank 處理
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
????if (!navigationAction.targetFrame.isMainFrame) {
????????[webView loadRequest:navigationAction.request];
????}
????return nil;
}
function showAlert()
????{
????????alert("被OC截獲到了");
????}