美文网首页ios工作
内置网页-WKWebView

内置网页-WKWebView

作者: 求长生 | 来源:发表于2019-08-02 22:56 被阅读0次

    WKWebView有 WKNavigationDelegate 和 WKUIDelegate 及WKScriptMessageHandler。WKNavigationDelegate主要处理一些跳转、加载处理操作,WKUIDelegate主要处理JS脚本,确认框,警告框等。因此WKNavigationDelegate更加常用。

    pragma mark --基本使用方法

    WKWebViewConfiguration:为添加WKWebView配置信息
    WKUserScript:用于进行JavaScript注入

        - (WKWebView *)webView {
        if (!_webView) {
            //创建网页配置对象
            WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc]init];
            //创建设置对象
            WKPreferences * preference = [[WKPreferences alloc]init];
            preference.minimumFontSize = 0;
            preference.javaScriptEnabled = YES;
            preference.javaScriptCanOpenWindowsAutomatically = YES; // 在iOS上默认为NO,表示是否允许不经过用户交互由javaScript自动打开窗口
            config.preferences = preference;
            config.allowsInlineMediaPlayback = YES; //allowsInlineMediaPlayback这个属性,默认为NO,如果不做更改,网页中内嵌的视频就无法正常播放
            config.requiresUserActionForMediaPlayback = YES;
            config.allowsPictureInPictureMediaPlayback = YES;
            config.applicationNameForUserAgent = @"Tutor";
            
            //自定义的WKScriptMessageHandler 是为了解决内存不释放的问题
            WeakWebViewScriptMessageDelegate * weakWebViewScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc]initWithDelegate:self];
            //native与JavaScript的交互管理,注册一个name的js方法为jsocModalT 设置处理接收JS方法的对象
            WKUserContentController * wkUContentController = [[WKUserContentController alloc]init];
            [wkUContentController addScriptMessageHandler:weakWebViewScriptMessageDelegate name:@"jsocModalT"];
            [wkUContentController addScriptMessageHandler:weakWebViewScriptMessageDelegate name:@"jsocAuthT"];
            [wkUContentController addScriptMessageHandler:weakWebViewScriptMessageDelegate name:@"jsocShareT"];
            [wkUContentController addScriptMessageHandler:weakWebViewScriptMessageDelegate name:@"jsocCloseWVT"];
            config.userContentController = wkUContentController;
            
            _webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) configuration:config];
            _webView.UIDelegate = self;
            _webView.navigationDelegate = self;
            _webView.allowsBackForwardNavigationGestures = YES; // 是否允许手势左滑返回上一级, 类似导航控制的左滑返回
        }
        return _webView;
        }
    

    pragma mark -- WKNavigationDelegate

    /*
    WKNavigationDelegate主要处理一些跳转、加载处理操作,WKUIDelegate主要处理JS脚本,确认框,警告框等
    */

        // 页面开始加载时调用
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
        
    }
    
    // 页面加载失败时调用
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
        [self.progressView setProgress:0.0f animated:NO];
        //无网络时;Code=-1009 "The Internet connection appears to be offline." UserInfo={NSUnderlyingError=0x280b69080 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)"
        //网页无法打开时:omain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={NSUnderlyingError=0x281105c20 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)"
        
        [self.navigationController setNavigationBarHidden:NO animated:NO];
    }
    
    // 当内容开始返回时调用
    - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
        
    }
    
    // 页面加载完成之后调用
    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    
    }
    
    //提交发生错误时调用
    - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
        [self.progressView setProgress:0.0f animated:NO];
    }
    
    // 接收到服务器跳转请求即服务重定向时之后调用
    - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
        
    }
    
    // 根据WebView对于即将跳转的HTTP请求头信息和相关信息来决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
        NSString * urlStr = navigationAction.request.URL.absoluteString;
        NSLog(@"发送跳转请求:%@",urlStr);
        //自己定义的协议头
        NSString *htmlHeadString = @"在收到跳转请求响应后,决定是否跳转";
        if ([urlStr hasPrefix:htmlHeadString]) {
            decisionHandler(WKNavigationActionPolicyCancel); //不允许跳转
        }else{
            decisionHandler(WKNavigationActionPolicyAllow); //允许跳转
        }
    }
    
    // 根据客户端受到的服务器响应头以及response相关信息来决定是否可以跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
        NSString * urlStr = navigationResponse.response.URL.absoluteString;
        NSLog(@"当前跳转地址:%@",urlStr);
        decisionHandler(WKNavigationResponsePolicyAllow);
    }
    
    //进程被终止时调用
    - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
        
    }
    

    pragma mark -- 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:@"" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
        [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completionHandler();
        }])];
        [self presentViewController:alertController animated:YES completion:nil];
    }
    
    - (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];
    }
    
    - (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];
    }
    
    // 创建一个新的WebView,内部点击链接跳转
    - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
        if (!navigationAction.targetFrame.isMainFrame) {
            [webView loadRequest:navigationAction.request];
        }
        return nil;
    }
    

    pragma mark -- OC调用JS

    h5只能传一个参数,如果需要多个参数就需要用字典或者json组装。

    - (void)ocToJs{
        NSString * authorization = [AccountTool person].authorization;
        NSDictionary * dic = [[NSDictionary alloc]initWithObjects:@[authorization] forKeys:@[@"authorization"]];
        NSString * str = [dic mj_JSONString]; //模型转字符串
        NSString * AuthJSString = [NSString stringWithFormat:@"jsocAuthL('%@')",str];
        [_webView evaluateJavaScript:AuthJSString completionHandler:^(id _Nullable data, NSError * _Nullable error) {
        }];
    }
    
    

    pragma mark -- JS调用OC

    WKUserContentController:这个类主要用来做native与JavaScript的交互管理
    WKScriptMessageHandler:这个协议类专门用来处理监听JavaScript方法从而调用原生OC方法,和WKUserContentController搭配使用。
    addScriptMessageHandler要和removeScriptMessageHandlerForName配套出现,否则会造成内存泄漏。

    1.注册JS方法:是用JS回调的方法前先要注册其方法。

           //native与JavaScript的交互管理,注册一个name的js方法为jsocModalT 设置处理接收JS方法的对象
            WKUserContentController * wkUContentController = [[WKUserContentController alloc]init];
            [wkUContentController addScriptMessageHandler:weakWebViewScriptMessageDelegate name:@"jsocModalT"];
            [wkUContentController addScriptMessageHandler:weakWebViewScriptMessageDelegate name:@"jsocAuthT"];
            [wkUContentController addScriptMessageHandler:weakWebViewScriptMessageDelegate name:@"jsocShareT"];
            [wkUContentController addScriptMessageHandler:weakWebViewScriptMessageDelegate name:@"jsocCloseWVT"];
            config.userContentController = wkUContentController;
    
    1. WKScriptMessageHandler协议,获取回调中的方法名及参数
    //通过接收JS传出消息的name进行捕捉的回调方法
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
        NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
        //用message.body获得JS传出的参数体
        NSDictionary * parameter = message.body;
        if ([message.name isEqualToString:@"jsocModalT"]) {
            UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"标题" message:parameter[@"title"] preferredStyle:UIAlertControllerStyleAlert];
            [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                [self ocToJsAlert:true];
            }])];
            [alertController addAction:([UIAlertAction actionWithTitle:@"NO" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                [self ocToJsAlert:false];
            }])];
            [self presentViewController:alertController animated:YES completion:nil];
        }else if ([message.name isEqualToString:@"jsocAuthT"]){
            [self ocToJsAuth];
        }else if ([message.name isEqualToString:@"jsocShareT"]){
            
        }else if ([message.name isEqualToString:@"jsocCloseWVT"]){
            [self.navigationController popViewControllerAnimated:YES];
        }
    }
    

    3.注册过的方法要Remove掉。

    - (void)dealloc{
        //移除注册的js方法
        [[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsocModalT"];
        [[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsocAuthT"];
        [[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsocShareT"];
        [[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsocCloseWVT"];
        //移除观察者
        [_webView removeObserver:self
                      forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
        [_webView removeObserver:self
                      forKeyPath:NSStringFromSelector(@selector(title))];
    }
    

    pragma mark --观察title和网页加载进度条

    //添加监测网页加载进度的观察者
        [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) {
            NSLog(@"网页加载进度 = %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];
        }
    }
    

    pragma mark --加载网址和加载本地Html和刷新

    //创建一个NSURLRequest 的对象
    NSMutableURLRequest *Request_zsj = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:string]];
        [self.webView loadRequest:Request_zsj];
    
    //加载本地Html
    NSString *path = [[NSBundle mainBundle]pathForResource:@"JStoOC.html" ofType:nil];
        NSString *htmlString = [[NSString alloc]initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
        [_webView loadHTMLString:htmlString baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
    
    //刷新
    [_webView reload];
    
    //页面前进
     [_webView goForward];
    
     //页面后退
     [_webView goBack];
            
    

    《文章仅做工作时笔记整理。如没帮到您,还可以看看一下网站》

    https://www.cnblogs.com/oc-bowen/p/5946383.html
    https://www.w3school.com.cn/index.html

    相关文章

      网友评论

        本文标题:内置网页-WKWebView

        本文链接:https://www.haomeiwen.com/subject/mabrdctx.html