zoukankan      html  css  js  c++  java
  • iOS-WKWebView的使用

    参考文章:http://www.cocoachina.com/ios/20180831/24753.html

    WK时苹果在iOS8.0之后推出的控件,相比于UIWebView:

    • 内存消耗少;
    • 解决了网页加载时的内存泄漏问题;
    • 与HTML页面的交互更方便;
    • 总之,其性能比UIWebView好很多。

    使用时,首先要添加头文件:

    #import <WebKit/WebKit.h>

    简单创建一个WKWebView:

        self.iWKWebView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-64)];
        //此处协议下面会讲到
        self.iWKWebView.navigationDelegate = self;
        self.iWKWebView.UIDelegate = self;
        self.iWKWebView.allowsBackForwardNavigationGestures = YES;
        NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        [self.iWKWebView loadRequest:request];
        [self.view addSubview:self.iWKWebView];

    基本用法和UIWebView差不多。

    这里介绍几个主要的类:

    • WKWebView
    • WKWebViewConfiguration

    • WKPreferences

    • WKUserContentController

    • WKWebsiteDataStore


    1. WKWebView:

    常用属性:

    // 导航代理
    @property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
    // UI代理
    @property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;
    // 页面标题, 一般使用KVO动态获取
    @property (nullable, nonatomic, readonly, copy) NSString *title;
    // 页面加载进度, 一般使用KVO动态获取
    @property (nonatomic, readonly) double estimatedProgress;
    // 可返回的页面列表, 已打开过的网页, 有点类似于navigationController的viewControllers属性
    @property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;
    // 页面url
    @property (nullable, nonatomic, readonly, copy) NSURL *URL;
    // 页面是否在加载中
    @property (nonatomic, readonly, getter=isLoading) BOOL loading;
    // 是否可返回
    @property (nonatomic, readonly) BOOL canGoBack;
    // 是否可向前
    @property (nonatomic, readonly) BOOL canGoForward;
    // WKWebView继承自UIView, 所以如果想设置scrollView的一些属性, 需要对此属性进行配置
    @property (nonatomic, readonly, strong) UIScrollView *scrollView;
    // 是否允许手势左滑返回上一级, 类似导航控制的左滑返回
    @property (nonatomic) BOOL allowsBackForwardNavigationGestures;
    //自定义UserAgent, 会覆盖默认的值 ,iOS 9之后有效
    @property (nullable, nonatomic, copy) NSString *customUserAgent

    常用方法:

    // 带配置信息的初始化方法
    // configuration 配置信息
    - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
    // 加载请求
    - (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
    // 加载HTML
    - (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
    // 返回上一级
    - (nullable WKNavigation *)goBack;
    // 前进下一级, 需要曾经打开过, 才能前进
    - (nullable WKNavigation *)goForward;
    // 刷新页面
    - (nullable WKNavigation *)reload;
    // 根据缓存有效期来刷新页面
    - (nullable WKNavigation *)reloadFromOrigin;
    // 停止加载页面
    - (void)stopLoading;
    // 执行JavaScript代码
    - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

    2. WKWebViewConfiguration:配置信息

    可以用配置信息来初始化WKWebView.

    属性有:

        //关于网页的设置
        @property (nonatomic, strong) WKPreferences *preferences;
        //JavaScript与原生交互的桥梁
        @property (nonatomic, strong) WKUserContentController *userContentController;
        //提供了网站所能使用的数据类型
        @property (nonatomic, strong) WKWebsiteDataStore *websiteDataStore API_AVAILABLE(macosx(10.11), ios(9.0));
        //是否允许播放媒体文件
        @property (nonatomic) BOOL allowsAirPlayForMediaPlayback API_AVAILABLE(macosx(10.11), ios(9.0));
        //是使用h5的视频播放器在线播放, 还是使用原生播放器全屏播放
        @property (nonatomic) BOOL allowsInlineMediaPlayback;
        //需要用户允许才能播放的媒体类型
        @property (nonatomic) WKAudiovisualMediaTypes mediaTypesRequiringUserActionForPlayback API_AVAILABLE(macosx(10.12), ios(10.0));

     

    2.1  WKPreference:

       WKPreferences *preference = [[WKPreferences alloc]init];
        //最小字体大小
        preference.minimumFontSize = 0;
        //是否支持javaScript,默认YES
        preference.javaScriptEnabled = YES;
        //是否允许不经过用户交互由javaScript自动打开窗口
        preference.javaScriptCanOpenWindowsAutomatically = YES;

     

    2.2  WKUserContentController

    // 注入JavaScript与原生交互协议
    // JS 端可通过 window.webkit.messageHandlers..postMessage() 发送消息
    - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
    // 移除注入的协议, 在deinit方法中调用
    - (void)removeScriptMessageHandlerForName:(NSString *)name;
    // 通过WKUserScript注入需要执行的JavaScript代码
    - (void)addUserScript:(WKUserScript *)userScript;
    // 移除所有注入的JavaScript代码
    - (void)removeAllUserScripts;

    使用WKUserContentController注入的交互协议, 需要遵循WKScriptMessageHandler协议, 在其协议方法中获取JavaScript端传递的事件和参数:

    JS调用OC:

    简单理解就是:[userController addScriptMessageHandler:self name:@"JSSendToOC"];//userController是一个WKUserContentController对象,‘JSSendToOC’是方法名,

    当JS端通过window.webkit.messageHandlers.JSSendToOC.postMessage()方法调用'JSSendToOC'方法时,我们可以通过下面的协议方法获取到JS端传过来的数据,做我们的操作。

    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
    WKScriptMessage包含了传递的协议名称及参数, 主要从下面的属性中获取:
    // 协议名称, 即上面的add方法传递的name
    @property (nonatomic, readonly, copy) NSString *name;
    // 传递的参数
    @property (nonatomic, readonly, copy) id body;

     OC调用JS:

       NSString *js = @"callJsFunction('hahaha')";
       [self.webView evaluateJavaScript:js completionHandler:^(id _Nullable response, NSError * _Nullable error) {
           NSLog(@"response:%@..error:%@",response,error);
       }];

    这里是调用了JS的‘callJsFunction’方法,这个方法名是随便起的。

     

    2.3  WKWebsiteDataStore

    WKWebsiteDataStore 提供了网站所能使用的数据类型,包括 cookies,硬盘缓存,内存缓存活在一些WebSQL的数据持久化和本地持久化。可通过 WKWebViewConfiguration类的属性 websiteDataStore 进行相关的设置。WKWebsiteDataStore相关的API也比较简单:

    // 默认的data store
    + (WKWebsiteDataStore *)defaultDataStore;
    // 如果为webView设置了这个data Store,则不会有数据缓存被写入文件
    // 当需要实现隐私浏览的时候,可使用这个
    + (WKWebsiteDataStore *)nonPersistentDataStore;
    // 是否是可缓存数据的,只读
    @property (nonatomic, readonly, getter=isPersistent) BOOL persistent;
    // 获取所有可使用的数据类型
    + (NSSet<NSString *> *)allWebsiteDataTypes;
    // 查找指定类型的缓存数据
    // 回调的值是WKWebsiteDataRecord的集合
    - (void)fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler;
    // 删除指定的纪录
    // 这里的参数是通过上面的方法查找到的WKWebsiteDataRecord实例获取的
    - (void)removeDataOfTypes:(NSSet<NSString *> *)dataTypes forDataRecords:(NSArray<WKWebsiteDataRecord *> *)dataRecords completionHandler:(void (^)(void))completionHandler;
    // 删除某时间后修改的某类型的数据
    - (void)removeDataOfTypes:(NSSet<NSString *> *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler;
    // 保存的HTTP cookies
    @property (nonatomic, readonly) WKHTTPCookieStore *httpCookieStore

    dataTypes:

    // 硬盘缓存
    WKWebsiteDataTypeDiskCache,
    // HTML离线web应用程序缓存
    WKWebsiteDataTypeOfflineWebApplicationCache,
    // 内存缓存
    WKWebsiteDataTypeMemoryCache,
    // 本地缓存
    WKWebsiteDataTypeLocalStorage,
    // cookies
    WKWebsiteDataTypeCookies,
    // HTML会话存储
    WKWebsiteDataTypeSessionStorage,
    //  IndexedDB 数据库
    WKWebsiteDataTypeIndexedDBDatabases,
    // WebSQL 数据库
    WKWebsiteDataTypeWebSQLDatabases

    dataRecord:

    // 展示名称, 通常是域名
    @property (nonatomic, readonly, copy) NSString *displayName;
    // 包含的数据类型
    @property (nonatomic, readonly, copy) NSSet<NSString *> *dataTypes;

    关于此类的简单使用:

    1.删除指定时间的所有类型数据

        NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
        NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
        [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
            // Done
            NSLog(@"释放");
        }];

    2.查找删除

        WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
        [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
            for (WKWebsiteDataRecord *record in records) {
                [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
                    // done
                }];
            }
        }];

    3.查找删除特定的内容

       WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
        [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
            for (WKWebsiteDataRecord *record in records) {
                if ([record.displayName isEqualToString:@"baidu"]) {
                    [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
                        // done
                    }];
                }
            }
        }];

    WKNavigationDelegate:

    #pragma mark - WKNavigationDelegate
    
    //请求加载之前,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
        NSLog(@"加载前允许跳转");
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    //开始加载时调用
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
        NSLog(@"开始加载");
    }
    //收到响应开始加载后,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
        NSLog(@"收到响应后允许跳转");
        decisionHandler(WKNavigationResponsePolicyAllow);
    }
    //内容开始返回时调用
    - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{
        NSLog(@"开始返回内容");
    }
    //加载完成时调用
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
        NSLog(@"加载完成");
        self.title = webView.title;
    }
    //加载失败调用
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{
        NSLog(@"加载失败");
    }
    
    //收到服务器重定向请求后调用
    - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
        NSLog(@"服务器重定向");
    }
    //当main frame最后下载数据失败时,会回调
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{
         NSLog(@"返回内容发生错误");
    }
    
    //用于授权验证的API,与AFN、UIWebView的授权验证API是一样的
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling,nil);
    }
    
    //当web content处理完成时,会回调
    - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)){
        NSLog(@"WebContent完成");
    }
    View Code

    这里放一个完整的WKWebView例子,仅供参考:

    //初始化WKPreferences,并设置相关属性
        WKPreferences *preference = [[WKPreferences alloc]init];
        
        //初始化WKUserContentController,并设置相关属性
        WKUserContentController *userController = [[WKUserContentController alloc]init];
    //    添加在js中操作的对象名称,通过该对象来向web view发送消息
    //    JS 端可通过 window.webkit.messageHandlers..postMessage() 发送消息
    //    <script type="text/javascript">
    //    function clickBtn(){
    //        var dict = {"name":"tom","age":"20"};
    //        window.webkit.messageHandlers.JSSendToOC.postMessage(dict);
    //    }
    //    </script>
        [userController addScriptMessageHandler:self name:@"JSSendToOC"];
        
        //初始化WKWebsiteDataStore,并设置相关属性
        WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
    //     如果为webView设置了这个data Store,则不会有数据缓存被写入文件
    //     当需要实现隐私浏览的时候,可使用这个
    //    WKWebsiteDataStore *dataStore = [WKWebsiteDataStore nonPersistentDataStore];
        
        //配置信息
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
        configuration.preferences = preference;
        configuration.userContentController = userController;
        configuration.websiteDataStore = dataStore;
        
        self.iWKWebView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-64) configuration:configuration];
    
        self.iWKWebView.navigationDelegate = self;
        self.iWKWebView.UIDelegate = self;
        
        self.iWKWebView.allowsBackForwardNavigationGestures = YES;
        NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        [self.iWKWebView loadRequest:request];
        [self.view addSubview:self.iWKWebView];

    再加一个知识点:WKWebView加载的时候添加一个自定义的进度条。

    此时我们需要获取到webview加载的进度数值。

    这里可以通过添加监听来获取。

    [self.iWKWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
    estimatedProgress是WKWebView的一个属性。
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        
        if ([keyPath isEqualToString:@"estimatedProgress"] && object==self.iWKWebView) {
            //获取到webview的进度数值,加载自定义的进度条
            //self.iWKWebView.estimatedProgress
        }
        
    }
  • 相关阅读:
    Ajax基础知识详解
    php 基础语法整理
    06 js-递归
    原生js 实现瀑布流布局
    解决高度坍塌问题
    jQuery动画效果
    jQuery增删元素类名
    JQueryTab栏切换(important!)
    jQuery排他思想(important!)
    jQuery设置元素样式
  • 原文地址:https://www.cnblogs.com/lfyDragon/p/9804923.html
Copyright © 2011-2022 走看看