• WKWebView讲解与使用


    随着IOS开发的应用,对于网页嵌入也越来越多了,在IOS 8之前我们使用UIWebView展示详情页,自从IOS 8之后就出现了WKWebView,相比UIWebView,WKWebView优化了较多的体验。下面将讲述WKWebView的知识点以及运用,大概需要花费10-20分钟时间,希望对大家有所帮助!!!

    一、WKWebView优点

    WKWebView采用跨进程方案,Nitro JS解析器,高达60fps的刷新率,理论上性能和Safari比肩,而且对H5也实现了高度支持。

    1.WKWebView的内存开销比UIWebView小很多

    2.内置手势

    3.支持了更多的HTML5特性

    4.有Safari相同的JavaScript引擎

    5.提供常用的属性,如加载网页进度的estimatedProgress属性

    下面来对比UIWebView和WKWebView的流程区别(左边是UIWebView,右边是WKWebView)

    WKWebView的流程粒度更加细致,不但在不但在请求的时候会询问WKWebView是否请求数据,还会在返回数据之后询问WKWebView是否加载数据。

    #请求数据的时候询问
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
    #返回数据的时候询问
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;

    在流程中,WKWebView返回的错误粒度也比UIWebView细:

    #请求数据时发生的error
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
    #请求之后加载H5发生的error
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;

    二、WKWebView基本使用

    2.1 基本使用

    2.1.1 使用WKWebView引用头文件

    - (void)setupWebview{
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        config.selectionGranularity = WKSelectionGranularityDynamic;
        config.allowsInlineMediaPlayback = YES;
        WKPreferences *preferences = [WKPreferences new];
        //是否支持JavaScript
        preferences.javaScriptEnabled = YES;
        //不通过用户交互,是否可以打开窗口
        preferences.javaScriptCanOpenWindowsAutomatically = YES;
        config.preferences = preferences;
        WKWebView *webview = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, KScreenHeight - 64) configuration:config];
        [self.view addSubview:webview];
         
        /* 加载服务器url的方法*/
        NSString *url = @"https://www.baidu.com";
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
        [webview loadRequest:request];
         
        webview.navigationDelegate = self;
        webview.UIDelegate = self;
    }

    WKWebViewConfiguration和WKPreferences中很多属性对WebView初始化进行设置。

    2.1.2 下面遵循协议和实现的协议方法:

    #pragma mark - 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 decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
        //允许跳转
        decisionHandler(WKNavigationActionPolicyAllow);
        //不允许跳转
        //decisionHandler(WKNavigationActionPolicyCancel);
    }
    /* 在收到响应后,决定是否跳转 */
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
         
        NSLog(@"%@",navigationResponse.response.URL.absoluteString);
        //允许跳转
        decisionHandler(WKNavigationResponsePolicyAllow);
        //不允许跳转
        //decisionHandler(WKNavigationResponsePolicyCancel);
    }

    三、WKWebView开发细节

    3.1 url 中文处理

    有时候我们加载的url中出现了中文,需要我们手动转码,但是同时又要保证URL中的特殊字符保持不变,那么我们可以使用下面的方法(方法)

    - (NSURL *)url{
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored"-Wdeprecated-declarations"
        return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))];
    #pragma clang diagnostic pop
    }

    3.2 获取h5中的标题以及添加进度条

    获取h5中的标题和添加进度条放在一起展示看起来更明朗一点,在初始化webView,添加两个观察者分别用来监听webView的estimateProgress和title属性

    webview.navigationDelegate = self;
    webview.UIDelegate = self;
         
    [webview addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
    [webview addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];

    添加创建进度条,并添加进度条图层属性:

    @property (nonatomic,weak) CALayer *progressLayer;
    
    -(void)setupProgress{
        UIView *progress = [[UIView alloc]init];
        progress.frame = CGRectMake(0, 0, KScreenWidth, 3);
        progress.backgroundColor = [UIColor  clearColor];
        [self.view addSubview:progress];
         
        CALayer *layer = [CALayer layer];
        layer.frame = CGRectMake(0, 0, 0, 3);
        layer.backgroundColor = [UIColor greenColor].CGColor;
        [progress.layer addSublayer:layer];
        self.progressLayer = layer;
    }

    实现观察者的回调方法:

    #pragma mark - KVO回馈
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<nskeyvaluechangekey,id> *)change context:(void *)context{
        if ([keyPath isEqualToString:@"estimatedProgress"]) {
            self.progressLayer.opacity = 1;
            if ([change[@"new"] floatValue] <[change[@"old"] floatValue]) {
                return;
            }
            self.progressLayer.frame = CGRectMake(0, 0, KScreenWidth*[change[@"new"] floatValue], 3);
            if ([change[@"new"]floatValue] == 1.0) {
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    self.progressLayer.opacity = 0;
                    self.progressLayer.frame = CGRectMake(0, 0, 0, 3);
                });
            }
        }else if ([keyPath isEqualToString:@"title"]){
            self.title = change[@"new"];
        }
    }</nskeyvaluechangekey,id>

    3.3 添加userAgent信息

    有时候h5的欧版需要我们为WebView的请求添加userAgent,用来识别操作系统等一下信息,但是如果每次用到webView都要添加一次的话会比较麻烦,下面是一种解决问题的办法

    在Appdelegate中添加一个WKWebView的属性,启动app时直接为该属性添加userAgent:

    - (void)setUserAgent {
        _webView = [[WKWebView alloc] initWithFrame:CGRectZero];
        [_webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
            if (error) { return; }
            NSString *userAgent = result;
            if (![userAgent containsString:@"/mobile-iOS"]) {
                userAgent = [userAgent stringByAppendingString:@"/mobile-iOS"];
                NSDictionary *dict = @{@"UserAgent": userAgent};
                [TKUserDefaults registerDefaults:dict];
            }
        }];
    }

    这样一来,在app创建webView时存在了我们添加的userAgent的信息。

    3.4 JS调用OC

    js会通过以下方法调用原生方法

    window.webkit.messageHandlers.<#对象名#>.postMessage(<#参数#>)

    在原生中我们只要实现WKScriptMessageHandler的代理方法就可以了,值得注意的是参数name需要与上述代码中对象名一致。

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

    最后在

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

    在这个方法中获取做下判断,响应对应的方法即可:

    // 初始化WKWebView,在实例化WKWebViewConfiguration对象的时候我们同时添加scriptMessageHandler
     //进行配置控制器
            WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
            //实例化对象
            configuration.userContentController = [WKUserContentController new];
            //调用JS方法
            [configuration.userContentController addScriptMessageHandler:self name:@"btnClick"];
    
    #pragma mark - WKScriptMessageHandler
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
        if ([message.name isEqualToString:@"btnClick"]) {
            NSDictionary *jsData = message.body;
            NSLog(@"%@", message.name, jsData);
            //读取js function的字符串
            NSString *jsFunctionString = jsData[@"result"];
            //拼接调用该方法的js字符串(convertDictionaryToJson:方法将NSDictionary转成JSON格式的字符串)
            NSString *jsonString = [NSDictionary convertDictionaryToJson:@{@"test":@"123", @"data":@"666"}];
            NSString *jsCallBack = [NSString stringWithFormat:@"(%@)(%@);", jsFunctionString, jsonString];
            //执行回调
            [self.weWebView evaluateJavaScript:jsCallBack completionHandler:^(id _Nullable result, NSError * _Nullable error) {
                if (error) {
                    NSLog(@"err is %@", error.domain);
                }
            }];
        }
    }

    以上需要注意的是,由于message的body只能是NSNumber,NSString,NSDate,NSArray,NSDictionary,NSNull这几种类型,我们无法将js函数直接原生,在需要进行回调的环境下,我们将js回调函数转为String后再传给原生,再由原生获取后进行回调操作,实际上这是已经进行了动态js注入。

    3.5 OC调用JS

    动态注入js方法就比较简单了,我们只要实现相应的方法就可以。

    - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

    下面有一段示例代码

    // 此处是设置需要调用的js方法以及将对应的参数传入,需要以字符串的形式
    NSString *jsFounction = [NSString stringWithFormat:@"getAppConfig('%@')", APP_CHANNEL_ID];
    // 调用API方法
        [self.weexWebView evaluateJavaScript:jsFounction completionHandler:^(id object, NSError * _Nullable error) {
            NSLog(@"obj:%@---error:%@", object, error);
        }];

    以上就是WKWebView的基本使用,希望大家对WKWebView的理解有所提高,谢谢!!!

  • 相关阅读:
    海量数据挖掘--DB优化篇
    BabyLinux制作过程详解
    C语言中的位域、字节序、比特序、大小端
    使用Busybox制作CRAMFS文件系统成功
    使用BusyBox制作linux根文件系统(CramFS+mdev)
    常用 U-boot命令详解
    基本C库函数
    simple_strtoul()
    在u-boot中添加命令
    U-Boot添加menu命令的方法及U-Boot命令执行过程
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/10234571.html
Copyright © 2020-2023  润新知