iOS 进阶之 WKWebView

作者: 路飞_Luck | 来源:发表于2018-06-21 20:31 被阅读30次
    前言

    Xcode8发布之后,编译器开始不支持 iOS7了.很多应用在适配 iOS10之后都不再适配 iOS7了.最低适配到 iOS8,所以第一个要更改的是用 WKWebView替换原来的UIWebView.并且 WKWebView 有很多明显的优势.

    • 更多的支持 HTML5的特性
    • 官方宣称的高达60fps 的滚动刷新率以及内置手势
    • 将 UIWebViewDelegate 与 UIWebView 拆分了14个类和3个协议,以前很多不方便实现的功能得以实现.文档
    • 和Safair 相同的 JavaScript 引擎
    • 占用更少的内存

    一 基本使用方法


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

    • 初始化 WKWebView
    - (void)drawUI {    
        WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
        _webView = webView;
        webView.UIDelegate = self;
        webView.navigationDelegate = self;
        [self.view addSubview:webView];
        
        [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
    }
    
    • WKNavigationDelegate代理方法
    // 页面开始加载时调用
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
        NSLog(@"didStartProvisionalNavigation");
    }
    
    // 当内容开始返回时调用
    - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
        NSLog(@"didCommitNavigation");
    }
    
    // 页面加载完成时调用
    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
        NSLog(@"didFinishNavigation");
        
        //say()是JS方法名,completionHandler是异步回调block
        [webView evaluateJavaScript:@"say()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
            NSLog(@"evaluateJavaScript %@",result);
        }];
    }
    
    // 页面加载失败时调用
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
        NSLog(@"didFailProvisionalNavigation");
    }
    
    // 接收到服务器跳转请求之后调用
    - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
        NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
    }
    
    // 在收到响应后,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
        NSLog(@"decidePolicyForNavigationResponse:%@",navigationResponse.response.URL.absoluteString);
        //允许跳转
        decisionHandler(WKNavigationResponsePolicyAllow);
        //不允许跳转
        //decisionHandler(WKNavigationResponsePolicyCancel);
    }
    
    // 在发送请求之前,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
        NSLog(@"decidePolicyForNavigationAction:%@",navigationAction.request.URL.absoluteString);
        //允许跳转
        decisionHandler(WKNavigationActionPolicyAllow);
        //不允许跳转
        //decisionHandler(WKNavigationActionPolicyCancel);
    }
    
    • WKUIDelegate代理方法
    // 创建一个新的 webView
    - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
        return [[WKWebView alloc] init];
    }
    
    // 输入框
    - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
        completionHandler(@"http");
    }
    
    // 确认框
    - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
        completionHandler(YES);
    }
    
    // 警告框
    - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
        completionHandler();
    }
    

    二 OC 与 JS 交互


    WKWebview提供了API实现js交互 不需要借助JavaScriptCore或者webJavaScriptBridge。使用WKUserContentController实现js native交互。简单的说就是先注册约定好的方法,然后再调用。

    JS 调用 OC 方法
    • WKDelegateViewController.h文件代码
    #import <UIKit/UIKit.h>
    #import <WebKit/WebKit.h>
    
    @protocol WKDelegate <NSObject>
    
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
    
    @end
    
    @interface WKDelegateViewController : UIViewController <WKScriptMessageHandler>
    
    /** 代理 */
    @property(nonatomic, weak) id<WKDelegate> delegate;
    
    @end
    
    • WKDelegateViewController.m 文件代码
    #import "WKDelegateViewController.h"
    
    @interface WKDelegateViewController ()
    
    @end
    
    @implementation WKDelegateViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    }
    
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
        if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {
            [self.delegate userContentController:userContentController didReceiveScriptMessage:message];
        }
    }
    
    @end
    
    • 主文件代码
    #import "WKWebViewController.h"
    #import <WebKit/WebKit.h>
    #import "WKDelegateViewController.h"
    
    @interface WKWebViewController ()<WKUIDelegate, WKNavigationDelegate,WKScriptMessageHandler,WKDelegate>
    
    @end
    
    @implementation WKWebViewController {
        WKWebView *_webView;
        WKUserContentController *_userContentController;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        [self setupData];
        [self drawUI];
    }
    
    - (void)dealloc {
        NSLog(@"WKWebViewController dealloc");
        //这里需要注意,前面增加过的方法一定要remove掉。
        [_userContentController removeScriptMessageHandlerForName:@"sayhello"];
    }
    
    - (void)setupData {
        self.view.backgroundColor = [UIColor whiteColor];
        self.title = @"WKWebView";
    }
    
    - (void)drawUI {
        // 配置环境
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
        _userContentController = [[WKUserContentController alloc] init];
        configuration.userContentController = _userContentController;
        // 注册方法
        WKDelegateViewController *delegateVC = [[WKDelegateViewController alloc] init];
        delegateVC.delegate = self;
        
        [_userContentController addScriptMessageHandler:delegateVC name:@"sayhello"];
        
        WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
        _webView = webView;
        webView.UIDelegate = self;
        webView.navigationDelegate = self;
        [self.view addSubview:webView];
        
        NSString *path = [[NSBundle mainBundle] pathForResource:@"Company-2" ofType:@"html"];
        [webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]];
    }
    
    #pragma mark - WKScriptMessageHandler
    
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
        NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
    }
    
    • h5代码
    <!DOCTYPE html>
    <html>
        <head>
            <script>
                function say()
                {
                    //前端需要用 window.webkit.messageHandlers.注册的方法名.postMessage({body:传输的数据} 来给native发送消息
                    window.webkit.messageHandlers.sayhello.postMessage({body: 'hello world!'});
                }
            </script>
        </head>
        <body>
            <h1>hello world</h1>
            <button onclick="say()">say hello</button>
        </body>
        
    </html>
    

    打印出的 log 日志:

    注意点

    • addScriptMessageHandler要和removeScriptMessageHandlerForName配套出现,否则会造成内存泄漏。
    • h5只能传一个参数,如果需要多个参数就需要用字典或者json组装。

    OC 调用 JS 方法

    代码如下

    // 页面加载完成时调用
    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
        NSLog(@"didFinishNavigation");
        
        //say()是JS方法名,completionHandler是异步回调block
        [webView evaluateJavaScript:@"say()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
            NSLog(@"evaluateJavaScript %@",result);
        }];
    }
    

    h5代码同上

    本文参考IOS进阶之WKWebView,非常感谢该作者
    项目连接地址

    相关文章

      网友评论

        本文标题:iOS 进阶之 WKWebView

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