iOS WKWebView 如何使用

apple官方文档https://developer.apple.com/documentation/webkit

WKWebView 是苹果在iOS 8中引入的新组件,目的是提供一个现代的支持最新Webkit功能的网页浏览控件,摆脱过去 UIWebView的老、旧、笨,特别是内存占用量巨大的问题。它使用与Safari中一样的Nitro JavaScript引擎,大大提高了页面js执行速度。

相比于UIWebView的优势: 

在性能、稳定性、占用内存方面有很大提升; 

允许JavaScript的Nitro库加载并使用(UIWebView中限制) 

增加加载进度属性:estimatedProgress,不用在自己写假进度条了 支持了更多的HTML的属性

具体分析WKWebView的优劣势

内存占用是UIWebView的1/4~1/3

页面加载速度有提升,有的文章说它的加载速度比UIWebView提升了一倍左右。

更为细致地拆分了 UIWebViewDelegate 中的方法

自带进度条。不需要像UIWebView一样自己做假进度条(通过NJKWebViewProgress和双层代理技术实现),技术复杂度和代码量,根贴近实际加载进度优化好的多。

允许JavaScript的Nitro库加载并使用(UIWebView中限制)

可以和js直接互调函数,不像UIWebView需要第三方库WebViewJavascriptBridge来协助处理和js的交互。

不支持页面缓存,需要自己注入cookie,而UIWebView是自动注入cookie。

无法发送POST参数问题

基本参数解释:

WKWebView:网页的渲染与展示,通过WKWebViewConfiguration可以进行自定义配置。

WKWebViewConfiguration:这个类专门用来配置WKWebView。

WKPreference:这个类用来进行相关webView设置。

WKProcessPool:这个类用来配置进程池,与网页视图的资源共享有关。

WKUserContentController:这个类主要用来做native与JavaScript的交互管理。

WKUserScript:用于进行JavaScript注入。

WKScriptMessageHandler:这个类专门用来处理JavaScript调用native的方法。

WKNavigationDelegate:网页跳转间的导航管理协议,这个协议可以监听网页的活动。

WKNavigationAction:网页某个活动的示例化对象。

WKUIDelegate:用于交互处理JavaScript中的一些弹出框。

WKBackForwardList:堆栈管理的网页列表。

WKBackForwardListItem:每个网页节点对象。

1.加载网页

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.进度条和标题

    _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];

//添加监测网页加载进度的观察者

    [self.webView addObserver:self

                   forKeyPath:NSStringFromSelector(@selector(estimatedProgress))

                      options:0

                      context:nil];

    [self.webView addObserver:self

                   forKeyPath:@"title"

                      options:NSKeyValueObservingOptionNew

                      context:nil];

//kvo 监听进度和标题 必须实现此方法

-(void)observeValueForKeyPath:(NSString *)keyPath

                     ofObject:(id)object

                       change:(NSDictionary<NSKeyValueChangeKey,id> *)change

                      context:(void *)context{


    if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))]

        && object == _webView) {


        DDLog(@"网页加载进度 = %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.刷新、上一页、下一页

- (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调用JS

//OC调用JS

- (void)ocToJs{

    //changeColor()是JS方法名,completionHandler是异步回调block

    NSString *jsString = [NSString stringWithFormat:@"changeColor('%@')", @"Js颜色参数"];

    [_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) {

        DDLog(@"改变HTML的背景色");

    }];


    //改变字体大小 调用原生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调用JS改变背景色

    function changeColor(parameter)

    {

        document.body.style.backgroundColor = randomColor();

    }

5.JS调用OC

//方式一.WKNavigationDelegate通过即将跳转的url拦截

// 根据WebView对于即将跳转的HTTP请求头信息和相关信息来决定是否跳转

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {


    NSString * urlStr = navigationAction.request.URL.absoluteString;

    NSLog(@"发送跳转请求:%@",urlStr);

    //自己定义的协议头

    NSString *htmlHeadString = @"github://";

    if([urlStr hasPrefix:htmlHeadString]){

        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"通过截取URL调用OC" message:@"你想前往我的Github主页?" preferredStyle:UIAlertControllerStyleAlert];

        [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {


        }])];

        [alertController addAction:([UIAlertAction actionWithTitle:@"打开" 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注册相应交互事件

//自定义的WKScriptMessageHandler 是为了解决内存不释放的问题

        WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];

        //这个类主要用来做native与JavaScript的交互管理

        WKUserContentController * wkUController = [[WKUserContentController alloc] init];

        //注册一个name为jsToOcNoPrams的js方法 设置处理接收JS方法的对象

        [wkUController addScriptMessageHandler:weakScriptMessageDelegate  name:@"jsToOcNoPrams"];

        [wkUController addScriptMessageHandler:weakScriptMessageDelegate  name:@"jsToOcWithPrams"];


        config.userContentController = wkUController;

- (void)dealloc{

    //移除注册的js方法

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

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

}

//被自定义的WKScriptMessageHandler在回调方法里通过代理回调回来,绕了一圈就是为了解决内存不释放的问题

//通过接收JS传出消息的name进行捕捉的回调方法

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{

    DDLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);

    //用message.body获得JS传出的参数体

    NSDictionary * parameter = message.body;

    //JS调用OC

    if([message.name isEqualToString:@"jsToOcNoPrams"]){

        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js调用到了oc" message:@"不带参数" 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调用到了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":"我是参数"});

    }

//方式三.WKUIDelegate捕获系统交互

/**

*  web界面中有弹出警告框时调用

*

*  @param webView          实现该代理的webview

*  @param message          警告框中的内容

*  @param completionHandler 警告框消失调用

*/

- (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];

}

// 确认框

//JavaScript调用confirm方法后回调的方法 confirm是js中的确定框,需要在block中把用户选择的情况传递进去

- (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调用prompt方法后回调的方法 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];

}

// 页面是弹出窗口 _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截获到了");

    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342