WKWebView使用过程中遇到的坑

前言

在以前,一直以为Hybrid App开发是一种略显简单的事,不会使用太多能发挥移动端原生本身优势的复杂API,后来在新公司的工作(半混合式开发)过程中,发现混合式开发也是很多坑... 或者说WKWebView好多坑...

以下所说的内容,参考链接上基本上都有,本文的叙述方式主要是结合自己的经历(自己踩过的总结总是那么的深刻...(\捂脸))
应该在开始混合开发之前就看下这篇文章的,结果真的是等自己踩坑踩了一遍,总结之后,发现这篇文章上都有....(\大哭)
参考链接2: https://www.jianshu.com/p/86d99192df68

目录

  1. 加载URL的 encode问题
  2. loadRequest造成的body数据丢失
  3. 使用WKUserContentController造成的内存泄漏问题
  4. WKWebView的白屏问题(拍照引起)
  5. NSURLProtocol(做网页缓存)
  6. WKWebView的截屏问题(做意见反馈)
  7. window.alert()引起的crash问题(暂时没遇到)
  8. WKWebView拦截协议
  9. User-Agent修改
  10. UI细节问题
    . wkwebview中 h5绝对布局不生效
    . iOS 12中WKWebView中表单 键盘弹起自动上移,导致的兼容问题
    . WKWebview会下移20
    . 页面滚动速率
    . 视频自动播放
    . goBack API问题
    · didFinishNavigation迟迟不调用

1. 加载URL的 encode问题

在数据网络请求或其他情况下,需要把URL中的一些特殊字符转换成UTF-8编码,比如:中文。解决无法加载的问题

编码:

iOS 9以前
stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding
ios9后对其方法进行了修改
stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLQueryAllowedCharacterSet]

解码

iOS 9以前
stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding
iOS 9以后
stringByRemovingPercentEncoding

总结:混合开发中,最好将所有的URL的编解码问题都交给前端或者后端来做,毕竟移动端发版太笨重了,最起码保证iOS与Android两端的处理一致,否则前端同学做处理就太麻烦了

2. loadRequest造成的body数据丢失

在 WKWebView 上通过 loadRequest 发起的 post 请求 body 数据会丢失:

//同样是由于进程间通信性能问题,HTTPBody字段被丢弃[request setHTTPMethod:@"POST"];
[request setHTTPBody:[@"bodyData" dataUsingEncoding:NSUTF8StringEncoding]];
[wkwebview loadRequest: request];

解决方案:见参考链接,目前暂无使用场景

3. 使用WKUserContentController造成的内存泄漏问题

  self -> webView -> WKWebViewConfiguration -> WKUserContentController -> self (addScriptMessageHandler)

__weak typeof(self) copy_self = self;
addScriptMessageHandler: copy_self  //不能解决问题

解决方案:
  单独创建一个类实现`WKScriptMessageHandler`协议,然后在该类中再创建一个协议,由self来实现协议
  即: `self -> webView -> WKWebViewConfiguration -> WKUserContentController -> weak delegate obj --delegate--> self `

示例代码:
 1.创建一个新类WeakScriptMessageDelegate

  #import 
  #import 
  @interface WeakScriptMessageDelegate : NSObject
    @property (nonatomic, weak) id scriptDelegate;
    - (instancetype)initWithDelegate:(id)scriptDelegate;
  @end

  @implementation WeakScriptMessageDelegate

  - (instancetype)initWithDelegate:(id)scriptDelegate {
      self = [super init];
      if (self) {
        _scriptDelegate = scriptDelegate;
      }
      return self;
  }

  - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
      [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
  }

  @end

  2.在我们使用WKWebView的控制器中引入我们创建的那个类,将注入js对象的代码改为:
  [config.userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:scriptMessage];

  3.在delloc方法中通过下面的方式移除注入的js对象
  [self.config.userContentController removeScriptMessageHandlerForName:scriptMessage];
  上面三步就可以解决控制器不能被释放的问题了

4. WKWebView的白屏问题(拍照引起)

WKWebView 自诩拥有更快的加载速度,更低的内存占用,但实际上 WKWebView 是一个多进程组件,Network Loading 以及 UI Rendering 在其它进程中执行。
换WKWebView加载网页后,App 进程内存消耗反而大幅下降,但是仔细观察会发现,Other Process 的内存占用会增加。在一些用 webGL 渲染的复杂页面,使用 WKWebView 总体的内存占用(App Process Memory + Other Process Memory)不见得比 UIWebView 少很多。

在 UIWebView 上当内存占用太大的时候,App Process 会 crash;
而在 WKWebView 上当总体的内存占用比较大的时候,WebContent Process 会 crash,从而出现白屏现象

# 解决方案:
  1. 借助 iOS 9以后 WKNavigtionDelegate 新增了一个回调函数:
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0));

当 WKWebView 总体内存占用过大,页面即将白屏的时候,系统会调用上面的回调函数,我们在该函数里执行[webView reload](这个时候 webView.URL 取值尚不为 nil)解决白屏问题。在一些高内存消耗的页面可能会频繁刷新当前页面,H5侧也要做相应的适配操作。

  1. 检测 webView.title 是否为空
    并不是所有H5页面白屏的时候都会调用上面的回调函数,比如,最近遇到在一个高内存消耗的意见反馈H5页面上 present 系统相机,拍照完毕后返回原来页面的时候出现白屏现象(拍照过程消耗了大量内存,导致内存紧张,WebContent Process 被系统挂起),但上面的回调函数并没有被调用。在WKWebView白屏的时候,另一种现象是 webView.titile 会被置空, 因此,可以在 viewWillAppear 的时候检测 webView.title 是否为空来 reload 页面。

注意:可能有的前端页面确实没写title标签,在前端移动端开发中是可能会有这种场景的,会造成页面反复刷新

综合以上两种方法可以解决绝大多数的白屏问题。

5. NSURLProtocol(做网页缓存)

见WKWebView中NSURLProtocol的使用以及对H5的缓存,这是利用NSURLProtocol做网页缓存以及带来的隐患。

6. WKWebView的截屏问题(做意见反馈)

WKWebView 下通过 -[CALayer renderInContext:]实现截屏的方式失效,需要通过以下方式实现截屏功能:

@implementation UIView (ImageSnapshot) 
- (UIImage*)imageSnapshot { 
    UIGraphicsBeginImageContextWithOptions(self.bounds.size,YES,self.contentScaleFactor); 
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; 
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return newImage; 
} 
@end

然而这种方式依然解决不了 webGL 页面的截屏问题,Safari 以及 Chrome 这两个全量切换到 WKWebView 的浏览器也存在同样的问题:对webGL 页面的截屏结果不是空白就是纯黑图片

7. window.alert()引起的crash问题(暂时没遇到)

8. WKWebView拦截协议

WKWebView内默认不允许iTunes、weixin等协议跳转
UIWebView打开ituns.apple.com、跳转到appStore,、拨打电话,、唤起邮箱等一系列操作UIWebView 自己处理不了会自动交给UIApplication 来处理。

WKWebView上述事件WKWebView 不会自动交给UIApplication 来处理,除此之外,js端通过window.open() 打开新的网页的动作也被禁掉了

9. User-Agent修改

不要擅自修改webView的User-Agent,务必要跟前端反复确认,是否有用UA来做一些设备区分,进而做一些系统、机型适配问题。

10. UI细节问题

# 1. wkwebview中 h5绝对布局不生效
_baseWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; (ios 11之后)`

然后:前端需要在meta标签中增加 **iPhoneX**的适配**---**适配方案**viewport-fit**:**cover**
# 2. iOS 12中WKWebView中表单 键盘弹起自动上移,导致的兼容问题

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

if(kSystemVersion < 12.0) {
    if (@available(iOS 11.0, *)) {
        _webview.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } 
}
if (@available(iOS 12.0, *)) {
     _webview.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic;
}
# 3. WKWebview会下移20

解决方案:

VC.automaticallyAdjustsScrollViewInsets = NO;  //iOS11以及以后失效
需要使用_webview.scrollView.contentInsetAdjustmentBehavior
# 顺带解释一下以下两个属性

关于extendedLayoutIncludesOpaqueBarsautomaticallyAdjustsScrollViewInsets

  • 这两个属性属于UIViewController
  • 默认情况下extendedLayoutIncludesOpaqueBars = false 扩展布局不包含导航栏
  • 默认情况下automaticallyAdjustsScrollViewInsets = true 自动计算滚动视图的内容边距
  • 但是,当 导航栏 是 不透明时,而tabBar为透明的时候,为了正确显示tableView的全部内容,需要重新设置这两个属性的值,然后设置contentInset(参考代码).

在iOS11 中, UIViewController的automaticallyAdjustsScrollViewInsets属性已经不再使用,我们需要使用UIScrollView的 contentInsetAdjustmentBehavior属性来替代它.

UIScrollViewContentInsetAdjustmentBehavior 是一个枚举类型,值有以下几种:

  • automatic 和scrollableAxes一样,scrollView会自动计算和适应顶部和底部的内边距并且在scrollView 不可滚动时,也会设置内边距.
  • scrollableAxes 自动计算内边距.
  • never不计算内边距
  • always 根据safeAreaInsets 计算内边距一般我们肯定需要设置为 never,我们自己来控制间距,但是在iOS 12的webView中,就会出现开始所说的问题,需要设置为automatic才能解决

调整WKWebView布局方式,避免调整webView.scrollView.contentInset。实际上,即便在 UIWebView 上也不建议直接调整webView.scrollView.contentInset的值.

# 4. 页面滚动速率

WKWebView 需要通过 scrollView delegate 调整滚动速率:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
 scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
}
# 5. 视频自动播放

WKWebView 需要通过WKWebViewConfiguration.mediaPlaybackRequiresUserAction设置是否允许自动播放,但一定要在 WKWebView 初始化之前设置,在 WKWebView 初始化之后设置无效。

# 6. goBack API问题

WKWebView 上调用 -[WKWebView goBack], 回退到上一个页面后不会触发window.onload()函数、不会执行JS。

# 7. didFinishNavigation迟迟不调用

明明看起来页面加载完全,却不调用(一般只发生在第一次进入该页面)。
解决方法:经过自定义NSURLProtocol,拦截所有的H5加载资源,并在didCompleteWithError中打印资源的加载情况,发现有图片资源,域名有问题
Error Domain=NSURLErrorDomain Code=-1003 "未能找到使用指定主机名的服务器。
DNS解析失败导致系统认定H5一直没加载完成,第二次再进入,系统缓存了DNS解析的映射记录,所以很快就认定资源错误,调用了didFinish方法。

你可能感兴趣的:(WKWebView使用过程中遇到的坑)