WKWebView

 

開發App的過程當中,經常會遇到在App內部加載網頁,一般用UIWebView加載。這個自iOS2開始使用的網頁加載器一直是開發的心病:加載速度慢,佔用內存多,優化困難。若是加載網頁多,還可能由於過量佔用內存而給系統kill掉。各類優化的方法效果也不那麼明顯(點擊查看經常使用優化方法)。html

iOS8之後,蘋果推出了新框架Webkit,提供了替換UIWebView的組件WKWebView。各類UIWebView的問題沒有了,速度更快了,佔用內存少了,一句話,WKWebView是App內部加載網頁的最佳選擇!java

先看下 WKWebView的特性:git

  1. 在性能、穩定性、功能方面有很大提高(最直觀的體現就是加載網頁是佔用的內存,模擬器加載百度與開源中國網站時,WKWebView佔用23M,而UIWebView佔用85M);
  2. 容許JavaScript的Nitro庫加載並使用(UIWebView中限制);
  3. 支持了更多的HTML5特性;
  4. 高達60fps的滾動刷新率以及內置手勢;
  5. 將UIWebViewDelegate與UIWebView重構成了14類與3個協議(查看蘋果官方文檔);

而後從如下幾個方面說下WKWebView的基本用法:github

  1. 加載網頁
  2. 加載的狀態回調
  3. 新的WKUIDelegate協議
  4. 動態加載並運行JS代碼
  5. webView 執行JS代碼
  6. JS調用App註冊過的方法

1、加載網頁

加載網頁或HTML代碼的方式與UIWebView相同,代碼示例以下:web

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
[self.view addSubview:webView];

2、加載的狀態回調 (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;
// 在發送請求以前,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

3、新的WKUIDelegate協議

這個協議主要用於WKWebView處理web界面的三種提示框(警告框、確認框、輸入框),下面是警告框的例子:app

/**
 *  web界面中有彈出警告框時調用
 *
 *  @param webView           實現該代理的webview
 *  @param message           警告框中的內容
 *  @param frame             主窗口
 *  @param completionHandler 警告框消失調用
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler;

4、動態加載並運行JS代碼

用於在客戶端內部加入JS代碼,並執行,示例以下:框架

// 圖片縮放的js代碼
NSString *js = @"var count = document.images.length;for (var i = 0; i < count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '張圖');";
// 根據JS字符串初始化WKUserScript對象
WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
// 根據生成的WKUserScript對象,初始化WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
[config.userContentController addUserScript:script];
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
[_webView loadHTMLString:@"<head></head><imgea src='http://www.nsu.edu.cn/v/2014v3/img/background/3.jpg' />"baseURL:nil];
[self.view addSubview:_webView];

5、webView 執行JS代碼

用戶調用用JS寫過的代碼,通常指服務端開發的:異步

//javaScriptString是JS方法名,completionHandler是異步回調block
[self.webView evaluateJavaScript:javaScriptString completionHandler:completionHandler];

6、JS調用App註冊過的方法

WKWebView裏面註冊供JS調用的方法,是經過WKUserContentController類下面的方法:

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

scriptMessageHandler是代理回調,JS調用name方法後,OC會調用scriptMessageHandler指定的對象。

JS在調用OC註冊方法的時候要用下面的方式:

window.webkit.messageHandlers.<name>.postMessage(<messageBody>)

注意,name(方法名)是放在中間的,messageBody只能是一個對象,若是要傳多個值,須要封裝成數組,或者字典。整個示例以下:

//OC註冊供JS調用的方法
[[_webView configuration].userContentController addScriptMessageHandler:self name:@"closeMe"];

//OC在JS調用方法作的處理
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    NSLog(@"JS 調用了 %@ 方法,傳回參數 %@",message.name,message.body);
}

//JS調用
    window.webkit.messageHandlers.closeMe.postMessage(null);

若是你在selfdealloc打個斷點,會發現self沒有釋放!這顯然是不行的!谷歌後看到一種解決方法,以下:

@interface WeakScriptMessageDelegate : NSObject<WKScriptMessageHandler>

@property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;

- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;

@end

@implementation WeakScriptMessageDelegate

- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate
{
    self = [super init];
    if (self) {
        _scriptDelegate = scriptDelegate;
    }
    return self;
}

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
}

@end

思路是另外建立一個代理對象,而後經過代理對象回調指定的self

WKUserContentController *userContentController = [[WKUserContentController alloc] init];    
[userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"closeMe"];

運行代碼,self釋放了,WeakScriptMessageDelegate卻沒有釋放啊啊啊!
還需在selfdealloc裏面 添加這樣一句代碼:

[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"closeMe"];

OK,圓滿解決問題!

目前,大多數App須要支持iOS7以上的版本,而WKWebView只在iOS8後才能用,因此須要一個兼容性方案,既iOS7下用UIWebView,iOS8後用WKWebView。這個庫提供了這種兼容性方案:https://github.com/wangyangcc/IMYWebView

 

 

 1 //
 2 //  DetailViewController.m
 3 //  TreeNavigation
 4 //
 5 //  Created by wky on 16/10/2017.
 6 //  Copyright © 2017 vector. All rights reserved.
 7 //
 8 
 9 #import "DetailViewController.h"
10 #import <WebKit/WebKit.h>
11 
12 @interface DetailViewController ()<WKNavigationDelegate>
13 
14 @property(nonatomic,strong) WKWebView* webView;
15 
16 @end
17 
18 @implementation DetailViewController
19 
20 - (void)viewDidLoad {
21     [super viewDidLoad];
22     // Do any additional setup after loading the view.
23     //添加WKWebView
24     self.webView = [[WKWebView alloc]initWithFrame:self.view.frame];
25     [self.view addSubview:self.webView];
26     self.webView.navigationDelegate = self;
27     
28     
29     //最開始加載不出來,兩個緣由,一是本身等待的時間過短。二是哈爾濱的百度百科url含有中文,
30     // 爲此將URL編碼格式改成UTL-8,用stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding這個函數轉換一下
31     
32     NSString *urlString =[_url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
33     NSURL* url = [NSURL  URLWithString:urlString];
34     NSURLRequest* request = [NSURLRequest requestWithURL:url];
35     [self.webView loadRequest:request];
36     
37 //
38 //    WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
39 //    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
40 //    [self.view addSubview:webView];
41 
42     
43     
44     
45      
46 }
47 
48 - (void)didReceiveMemoryWarning {
49     [super didReceiveMemoryWarning];
50     // Dispose of any resources that can be recreated.
51     
52     
53     
54 }
55 
56 /*
57 #pragma mark - Navigation
58 
59 // In a storyboard-based application, you will often want to do a little preparation before navigation
60 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
61     // Get the new view controller using [segue destinationViewController].
62     // Pass the selected object to the new view controller.
63 }
64 */
65 
66 @end
相關文章
相關標籤/搜索