美文网首页收藏
iOS开发 - WKWebView

iOS开发 - WKWebView

作者: lionsom_lin | 来源:发表于2019-01-18 18:51 被阅读54次

    分为几大块:

    • JS 与OC 交互的方式有哪些?
    • WKWebView相关API介绍
    • WKWebView加载本地HTML
    • 进度条显示(KVO获取其他属性,如:title)
    • 使用中一些问题

    JS 与OC 交互的方式有哪些?

    1、在JS 中做一次URL跳转,然后在OC中拦截跳转。(这里分为UIWebView 和 WKWebView两种);
    2、利用WKWebView 的 MessageHandler(WKWebView 是 iOS8推出的);
    3、利用系统库 JavaScriptCore 来和UIWebView做相互调用(JavaScriptCore iOS 7推出的);
    4、利用第三方库WebViewJavascriptBridge;
    5、利用第三方cordova库,以前叫PhoneGap(这是一个库平台的库);
    JS_OC

    JS与OC交互 - URL拦截

    iOS下JS与OC互相调用(一)--UIWebView 拦截URL
    iOS下JS与OC互相调用(二)--WKWebView 拦截URL

    JS与OC交互 - 利用WKWebView的MessageHandler

    iOS下JS与OC互相调用(三)--WKWebView的MessageHandler

    JS与OC交互 - UIWebView与系统库JavaScriptCore

    iOS下JS与OC互相调用(四)--UIWebView与JavaScriptCore

    JS与OC交互 - 第三方库WebViewJavascriptBridge

    iOS下JS与OC互相调用(五)--UIWebView + WebViewJavascriptBridge
    iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge

    JS与OC交互 - 第三方cordova库

    iOS下JS与OC互相调用(七)--Cordova 基础
    iOS下JS与OC互相调用(八)--Cordova详解+实战

    WKWebView相关API介绍

    WKWebView
    @interface WKWebView : UIView
    Starting in iOS 8.0 and OS X 10.10, use WKWebView to add web content to your app. Do not use UIWebView or WebView.
    
    
    // 初始化webview相关API
    // 拷贝一个configuration来初始化一个网页使用。它是一个属性的集合,这个集合用来初始化网页。
    @property(nonatomic, readonly, copy) WKWebViewConfiguration *configuration;
    // 初始化一个网页视图,设置其大小和配置
    - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration;
    // 从解档数据获取一个初始化的对象。
    - (nullable instancetype)initWithCoder:(NSCoder *)coder;
    
    
    // 查看webview信息相关API
    // WKWebView继承自UIView, 所以如果想设置scrollView的一些属性, 需要对此属性进行配置
    @property (nonatomic, readonly, strong) UIScrollView *scrollView;
    // 页面标题, 一般使用KVO动态获取
    @property (nullable, nonatomic, readonly, copy) NSString *title;
    // 当前请求的URL,支持KVO的
    @property (nullable, nonatomic, readonly, copy) NSURL *URL;
    // 自定义UserAgent, 会覆盖默认的值      API_AVAILABLE(macosx(10.11), ios(9.0))
    @property (nullable, nonatomic, copy) NSString *customUserAgent;
    // 当前提交的导航的SecTrustRef对象,支持KVO。       API_AVAILABLE(macosx(10.12), ios(10.0))
    @property (nonatomic, readonly, nullable) SecTrustRef serverTrust;
    
    
    // 两个delegaete
    // The web view's navigation delegate.
    @property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
    // The web view's user interface delegate.
    @property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;
    
    
    // 加载content相关API
    // 页面加载进度, 一般使用KVO动态获取
    @property (nonatomic, readonly) double estimatedProgress;
    // 标识页面中的所有资源是否通过安全加密连接来加载,支持KVO的
    @property (nonatomic, readonly) BOOL hasOnlySecureContent;
    // 当前是否在加载内容
    @property (nonatomic, readonly, getter=isLoading) BOOL loading;
    // 重新加载当前页面。
    - (nullable WKNavigation *)reload;
    // 重新加载当前页面,如果可能,使用缓存验证条件执行端到端重新验证。
    - (nullable WKNavigation *)reloadFromOrigin;
    // 停止加载当前页面上的所有资源。
    - (void)stopLoading;
    // 加载请求API
    - (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
    // 加载URL     API_AVAILABLE(macosx(10.11), ios(9.0))
    - (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL;
    // 直接加载HTML
    - (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
    // 直接加载data     API_AVAILABLE(macosx(10.11), ios(9.0))
    - (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL;
    
    
    // Scaling Content 缩放相关API
    #if !TARGET_OS_IPHONE
    // 标识是否支持放大手势,默认为NO
    @property (nonatomic) BOOL allowsMagnification;
    // 放大因子,默认为1
    @property (nonatomic) CGFloat magnification;
    // 根据设置的缩放因子来缩放页面,并居中显示结果在指定的点
    - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point;
    #endif
    
    
    // 导航相关API
    // 标识是否支持左、右swipe手势是否可以前进、后退
    @property (nonatomic) BOOL allowsBackForwardNavigationGestures;
    // 是个WKBackForwardList对象,记录网页中跳转的记录
    @property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;
    //  是否可以招待goback操作,它是支持KVO的
    @property (nonatomic, readonly) BOOL canGoBack;
    // 是否可以执行gofarward操作,支持KVO
    @property (nonatomic, readonly) BOOL canGoForward;
    // 返回上一页面,如果不能返回,则什么也不干
    - (nullable WKNavigation *)goBack;
    // 进入下一页面,如果不能前进,则什么也不干
    - (nullable WKNavigation *)goForward;
    /* 前进/后退到一个指定的记录上
    item必须是WebView的WKBackForwardList中的一个记录
    返回一个新的WKNavigation对象
    如果已经在当前记录,或者没有找到,则会返回nil
    */ 
    - (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;
    /*  API_AVAILABLE(macosx(10.11), ios(9.0))
    在iOS上,这个属性只在支持3D Touch的设备上支持
    在iOS 10及以后的系统版本默认值是YES,之前的默认值是NO
    如果将该属性的值设置为YES,则iOS用户可以按下链接来预览链接,并可以检测到地址和电话号码等数据。这样的预览被用户称为peeks
    如果用户在链接预览上更深入地按下,预览将pop到目标网址。该动作会将应用程序切换到Safari
    如果您想在iOS中支持链接预览,但又想要在应用程序中保留用户,那么您可以将WKWebView类转换为SFSafariViewController类
    如果您使用WebView作为应用内浏览器,那么进行这种更改是最佳选择
    SFSafariViewController自动支持链接预览
    iOS 10.0+开始支持自定义链接预览,你也可以通过这种方式来实现用户预览网页并pop后仍然留在应用内而不用切换到Safari,只是你需要做一些额外的适配工作
    */
    @property (nonatomic) BOOL allowsLinkPreview;
    
    
    // 执行JavaScript
    - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
    
    
    // 截屏
    #if TARGET_OS_IPHONE
    - (void)takeSnapshotWithConfiguration:(nullable WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void (^)(UIImage * _Nullable snapshotImage, NSError * _Nullable error))completionHandler API_AVAILABLE(ios(11.0));
    
    WKWebViewConfiguration

    WKWebViewConfiguration只会在webview第一次初始化的时候使用,你不能用此类来改变一个已经初始化完成的webview的配置。

    // OC与JS交互使用,通过此属性来执行JavaScript代码来修改页面的行为
    @property (nonatomic, strong) WKUserContentController *userContentController;
    
    //***********下面属性一般不需要设置
    // 首选项设置,  
    //可设置最小字号, 是否允许执行js
    //是否通过js自动打开新的窗口
    @property (nonatomic, strong) WKPreferences *preferences;
    // 是否允许播放媒体文件
    @property (nonatomic) BOOL allowsAirPlayForMediaPlayback
    // 需要用户来操作才能播放的多媒体类型
    @property (nonatomic) WKAudiovisualMediaTypes mediaTypesRequiringUserActionForPlayback
    // 是使用h5的视频播放器在线播放, 还是使用原生播放器全屏播放
    @property (nonatomic) BOOL allowsInlineMediaPlayback;
    ......
    
    WKUserContentController

    WKUserContentControllerJavaScript与原生进行交互的桥梁, 主要使用的方法有:

    // 注入JavaScript与原生交互协议
    // JS 端可通过 window.webkit.messageHandlers.<name>.postMessage(<messageBody>) 发送消息
    - (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端传递的事件和参数:

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

    WKScriptMessage 包含了传递的协议名称及参数, 主要从下面的属性中获取:

    // 协议名称, 即上面的add方法传递的name
    @property (nonatomic, readonly, copy) NSString *name;
    // 传递的参数
    @property (nonatomic, readonly, copy) id body;
    // 发送消息的webView
    @property (nullable, nonatomic, readonly, weak) WKWebView *webView;
    //发送消息的帧
    @property (nonatomic, readonly, copy) WKFrameInfo *frameInfo;
    
    WKPreferences
    // webView的偏好设置
    //  minimum font size in points    default is 0;
    @property (nonatomic) CGFloat minimumFontSize;
    // 是否启用javaScript   default value is YES.
    @property (nonatomic) BOOL javaScriptEnabled;
    // 不通过用户交互,是否可以打开窗口
    // default NO in iOS and YES in OS X.
    @property (nonatomic) BOOL javaScriptCanOpenWindowsAutomatically;
    .......
    
    WKNavigationDelegate
    // 当WevView的内容开始加载时触发
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
    // 当页面加载内容过程中发生错误时触发
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
    // 当WebView开始接收网页内容时触发
    -(void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
    // 当前页面加载完成后触发
    -(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
    // 页面发生错误时触发
    -(void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error;
    // 当WebContent进程中止时触发
    - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView;
    /* 决定是否允许或者取消一次页面加载请求
    WKNavigationAction描述了触发本次请求的行为
    decisionHandler block在app已经决定允许或取消页面请求时调用
    这个block携带一个参数,是WKNavigationActionPolicy枚举类型
    WKNavigationActionPolicy枚举包括WKNavigationActionPolicyCancel取消和WKNavigationActionPolicyAllow允许两个枚举值
    你可以立即调用该block或者保存block并在以后的时间异步调用它
    */
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
    /* 在已经收到response时决定是否允许或者取消页面的加载
    WKNavigationResponse描述了response的信息
    decisionHandler block在app已经决定允许或取消页面加载时调用
    这个block携带一个参数,是WKNavigationResponsePolicy枚举类型
    WKNavigationResponsePolicy枚举类型包括WKNavigationResponsePolicyCancel取消和WKNavigationResponsePolicyAllow允许两个枚举值
    你可以立即调用该block或者保存block并在以后的时间异步调用它。
    */
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
    // 当WebView需要响应网页的登录请求时触发
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
    
    
    WKUIDelegate
    // 创建新的webview
    // 可以指定配置对象、导航动作对象、window特性
    - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
    // webview关闭时回调
    - (void)webViewDidClose:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);
    // 调用JS的alert()方法
    - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
    // 调用JS的confirm()方法
    - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
    // 调用JS的prompt()方法
    - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
    

    WKWebView加载本地HTML

    iOS中wkwebview加载本地html的要点
    iOS之在webView中引入本地html,image,js,css文件的方法

    方法一:WKWebView加载网页方法:loadRequest:

    本地HTML路径获取方式有两种:pathForResourceURLForResource

    NSString * localurl_1 = [[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:[[NSURL alloc] initWithString:localurl_1]];
    [_wkWebView loadRequest:request];
    
    NSURL * localurl = [[NSBundle mainBundle] URLForResource:@"error" withExtension:@"html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:localurl];
    [_wkWebView loadRequest:request];
    

    WKWebView加载后发现 pathForResource 加载失败,可以看出:
    1、pathForResource 的结果是纯路径
    2、URLForResource 的结果是加file协议的路径

    本地HTML的路径
    方法二:WKWebView加载网页方法:loadHTMLString: baseURL:

    这种方式就是使用pathForResource来获取路径

    NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
    NSString *localHtml = [NSString stringWithContentsOfFile:urlStr encoding:NSUTF8StringEncoding error:nil];
    NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
    [_wkWebView loadHTMLString:localHtml baseURL:fileURL];
    

    进度条显示(顺带获取title)

    在适当的位置添加KVO观察者,离开时要移除

    -(void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        // KVO 观察WebView 进度条 和 title
        [_wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
        [_wkWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
    }
    
    -(void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
        // 移除观察者
        [_wkWebView removeObserver:self forKeyPath:@"estimatedProgress"];
        [_wkWebView removeObserver:self forKeyPath:@"title"];
    }
    

    创建进度条

    // Progress 进度条
        _progressView = [[UIProgressView alloc] init];
        _progressView.tintColor = [UIColor greenColor];
        _progressView.trackTintColor = [UIColor clearColor];
        [self.view addSubview:_progressView];
    

    在KVO回调中修改页面

    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        if ([keyPath isEqualToString:@"estimatedProgress"]) {
            if ([change[@"new"] floatValue] < [change[@"old"] floatValue]) {
                return;
            }
            if ([change[@"new"] floatValue] == 1.0) {
                [_progressView setProgress:1.0 animated:YES];
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    _progressView.hidden = YES;
                    [_progressView setProgress:0 animated:NO];
                });
            } else {
                _progressView.hidden = NO;
                [_progressView setProgress:[change[@"new"] floatValue] animated:YES];
            }
        } else if ([keyPath isEqualToString:@"title"]){
            self.title = change[@"new"];
        }
    }
    

    日常使用中的一些问题

    WKWebView 那些坑

    1、wkwebview中 h5绝对布局不生效

     //新特性,解决偏移的问题
        if (@available(iOS 11.0, *)) {
            _wkWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        } else {
            self.automaticallyAdjustsScrollViewInsets = NO;
        }
    

    然后:前端需要在meta标签中增加 iPhoneX的适配---适配方案viewport-fitcover

    2、iOS 12中WKWebView中表单 键盘弹起自动上移,导致的兼容问题
    WKWebView会自动监听键盘弹出,并做上下移动处理(效果如同IQKeyboardManage这些库),但是在iOS12中会有一些问题,键盘收起后,控件不恢复原状,或者部分控件消失等不兼容问题
    解决方案:

     // iOS 12中WKWebView中表单 键盘弹起自动上移,但键盘下移webview不恢复
    if (@available(iOS 12.0, *)) {
        _wkWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic;
    }
    

    参考文档

    简介
    WKWebView详解
    WKWebViewTips
    [iOS]WKWebView的使用--API篇
    WKWebView浅析
    WKWebView代理方法解析

    使用中的问题
    iOS 11 WKWebView新特性
    iOS WKWebView无法处理URL Scheme和App Store链接的问题解决
    WKWebView使用过程中遇到的坑

    iOS中UIWebView与WKWebView、JavaScript与OC交互、Cookie管理看我就够(上)
    iOS中UIWebView与WKWebView、JavaScript与OC交互、Cookie管理看我就够(中)
    iOS中UIWebView与WKWebView、JavaScript与OC交互、Cookie管理看我就够(下)

    相关文章

      网友评论

        本文标题:iOS开发 - WKWebView

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