服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - IOS - iOS WKWebview 白屏检测实现的示例

iOS WKWebview 白屏检测实现的示例

2021-06-04 16:09BBTIME IOS

这篇文章主要介绍了iOS WKWebview 白屏检测实现的示例,帮助大家更好的进行ios开发,感兴趣的朋友可以了解下

前言

    自ios8推出wkwebview以来,极大改善了网页加载速度及内存泄漏问题,逐渐全面取代笨重的UIWebview。尽管高性能、高刷新的WKWebview在混合开发中大放异彩表现优异,但加载网页过程中出现异常白屏的现象却仍然屡见不鲜,且现有的api协议处理捕捉不到这种异常case,造成用户无用等待体验很差。
    针对业务场景需求,实现加载白屏检测。考虑采用字节跳动团队提出的webview优化技术方案。在合适的加载时机对当前webview可视区域截图,并对此快照进行像素点遍历,如果非白屏颜色的像素点超过一定的阈值,认定其为非白屏,反之重新加载请求。

获取快照

    ios官方提供了简易的获取webview快照接口,通过异步回调拿到当前可视区域的屏幕截图。

?
1
- (void)takeSnapshotWithConfiguration:(nullable WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void (^)(UIImage * _Nullable snapshotImage, NSError * _Nullable error))completionHandler API_AVAILABLE(ios(11.0));

其中snapshotConfiguration 参数可用于配置快照大小范围,默认截取当前客户端整个屏幕区域。由于可能出现导航栏成功加载而内容页却空白的特殊情况,导致非白屏像素点数增加对最终判定结果造成影响,考虑将其剔除。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)judgeLoadingStatus:(WKWebView *)webview {
  if (@available(iOS 11.0, *)) {
    if (webView && [webView isKindOfClass:[WKWebView class]]) {
 
      CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height; //状态栏高度
      CGFloat navigationHeight = webView.viewController.navigationController.navigationBar.frame.size.height; //导航栏高度
      WKSnapshotConfiguration *shotConfiguration = [[WKSnapshotConfiguration alloc] init];
      shotConfiguration.rect = CGRectMake(0, statusBarHeight + navigationHeight, _webView.bounds.size.width, (_webView.bounds.size.height - navigationHeight - statusBarHeight)); //仅截图检测导航栏以下部分内容
      [_webView takeSnapshotWithConfiguration:shotConfiguration completionHandler:^(UIImage * _Nullable snapshotImage, NSError * _Nullable error) {
        //todo
      }];
    }
  }
}

缩放快照

为了提升检测性能,考虑将快照缩放至1/5,减少像素点总数,从而加快遍历速度。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (UIImage *)scaleImage: (UIImage *)image {
  CGFloat scale = 0.2;
  CGSize newsize;
  newsize.width = floor(image.size.width * scale);
  newsize.height = floor(image.size.height * scale);
  if (@available(iOS 10.0, *)) {
    UIGraphicsImageRenderer * renderer = [[UIGraphicsImageRenderer alloc] initWithSize:newsize];
     return [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
            [image drawInRect:CGRectMake(0, 0, newsize.width, newsize.height)];
         }];
  }else{
    return image;
  }
}

缩小前后性能对比(实验环境:iPhone11同一页面下):

缩放前白屏检测:

iOS WKWebview 白屏检测实现的示例

iOS WKWebview 白屏检测实现的示例

耗时20ms

缩放后白屏检测:

iOS WKWebview 白屏检测实现的示例

iOS WKWebview 白屏检测实现的示例

耗时13ms

     注意这里有个小坑。由于缩略图的尺寸在 原图宽高*缩放系数后可能不是整数,在布置画布重绘时默认向上取整,这就造成画布比实际缩略图大(混蛋啊 摔!)。在遍历缩略图像素时,会将图外画布上的像素纳入考虑范围,导致实际白屏页 像素占比并非100% 如图所示。因此使用floor将其尺寸大小向下取整。

遍历快照

    遍历快照缩略图像素点,对白色像素(R:255 G: 255 B: 255)占比大于95%的页面,认定其为白屏。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
- (BOOL)searchEveryPixel:(UIImage *)image {
  CGImageRef cgImage = [image CGImage];
  size_t width = CGImageGetWidth(cgImage);
  size_t height = CGImageGetHeight(cgImage);
  size_t bytesPerRow = CGImageGetBytesPerRow(cgImage); //每个像素点包含r g b a 四个字节
  size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
 
  CGDataProviderRef dataProvider = CGImageGetDataProvider(cgImage);
  CFDataRef data = CGDataProviderCopyData(dataProvider);
  UInt8 * buffer = (UInt8*)CFDataGetBytePtr(data);
 
  int whiteCount = 0;
  int totalCount = 0;
 
  for (int j = 0; j < height; j ++ ) {
    for (int i = 0; i < width; i ++) {
      UInt8 * pt = buffer + j * bytesPerRow + i * (bitsPerPixel / 8);
      UInt8 red  = * pt;
      UInt8 green = *(pt + 1);
      UInt8 blue = *(pt + 2);
//      UInt8 alpha = *(pt + 3);
 
      totalCount ++;
      if (red == 255 && green == 255 && blue == 255) {
        whiteCount ++;
      }
    }
  }
  float proportion = (float)whiteCount / totalCount ;
  NSLog(@"当前像素点数:%d,白色像素点数:%d , 占比: %f",totalCount , whiteCount , proportion );
  if (proportion > 0.95) {
    return YES;
  }else{
    return NO;
  }
}

总结

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
typedef NS_ENUM(NSUInteger,webviewLoadingStatus) {
 
  WebViewNormalStatus = 0, //正常
 
  WebViewErrorStatus, //白屏
 
  WebViewPendStatus, //待决
};
 
// 判断是否白屏
- (void)judgeLoadingStatus:(WKWebview *)webview withBlock:(void (^)(webviewLoadingStatus status))completionBlock{
  webviewLoadingStatus __block status = WebViewPendStatus;
  if (@available(iOS 11.0, *)) {
    if (webview && [webview isKindOfClass:[WKWebView class]]) {
 
      CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height; //状态栏高度
      CGFloat navigationHeight = webview.viewController.navigationController.navigationBar.frame.size.height; //导航栏高度
      WKSnapshotConfiguration *shotConfiguration = [[WKSnapshotConfiguration alloc] init];
      shotConfiguration.rect = CGRectMake(0, statusBarHeight + navigationHeight, webview.bounds.size.width, (webview.bounds.size.height - navigationHeight - statusBarHeight)); //仅截图检测导航栏以下部分内容
      [webview takeSnapshotWithConfiguration:shotConfiguration completionHandler:^(UIImage * _Nullable snapshotImage, NSError * _Nullable error) {
        if (snapshotImage) {
          CGImageRef imageRef = snapshotImage.CGImage;
          UIImage * scaleImage = [self scaleImage:snapshotImage];
          BOOL isWhiteScreen = [self searchEveryPixel:scaleImage];
          if (isWhiteScreen) {
            status = WebViewErrorStatus;
          }else{
            status = WebViewNormalStatus;
          }
        }
        if (completionBlock) {
          completionBlock(status);
        }
      }];
    }
  }
}
 
// 遍历像素点 白色像素占比大于95%认定为白屏
- (BOOL)searchEveryPixel:(UIImage *)image {
  CGImageRef cgImage = [image CGImage];
  size_t width = CGImageGetWidth(cgImage);
  size_t height = CGImageGetHeight(cgImage);
  size_t bytesPerRow = CGImageGetBytesPerRow(cgImage); //每个像素点包含r g b a 四个字节
  size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
 
  CGDataProviderRef dataProvider = CGImageGetDataProvider(cgImage);
  CFDataRef data = CGDataProviderCopyData(dataProvider);
  UInt8 * buffer = (UInt8*)CFDataGetBytePtr(data);
 
  int whiteCount = 0;
  int totalCount = 0;
 
  for (int j = 0; j < height; j ++ ) {
    for (int i = 0; i < width; i ++) {
      UInt8 * pt = buffer + j * bytesPerRow + i * (bitsPerPixel / 8);
      UInt8 red  = * pt;
      UInt8 green = *(pt + 1);
      UInt8 blue = *(pt + 2);
//      UInt8 alpha = *(pt + 3);
 
      totalCount ++;
      if (red == 255 && green == 255 && blue == 255) {
        whiteCount ++;
      }
    }
  }
  float proportion = (float)whiteCount / totalCount ;
  NSLog(@"当前像素点数:%d,白色像素点数:%d , 占比: %f",totalCount , whiteCount , proportion );
  if (proportion > 0.95) {
    return YES;
  }else{
    return NO;
  }
}
 
//缩放图片
- (UIImage *)scaleImage: (UIImage *)image {
  CGFloat scale = 0.2;
  CGSize newsize;
  newsize.width = floor(image.size.width * scale);
  newsize.height = floor(image.size.height * scale);
  if (@available(iOS 10.0, *)) {
    UIGraphicsImageRenderer * renderer = [[UIGraphicsImageRenderer alloc] initWithSize:newsize];
     return [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
            [image drawInRect:CGRectMake(0, 0, newsize.width, newsize.height)];
         }];
  }else{
    return image;
  }
}

仅需在合适的view生命周期内回调使用该函数方法即可检测出页面状态是否白屏,且性能损耗可忽略不计。

声明

作者:BBTime
链接:https://juejin.im/post/6885298718174609415

以上就是iOS WKWebview 白屏检测实现的示例的详细内容,更多关于iOS WKWebview 白屏检测的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.im/post/6885298718174609415?utm_source=tuicool&utm_medium=referral

延伸 · 阅读

精彩推荐
  • IOSiOS中MD5加密算法的介绍和使用

    iOS中MD5加密算法的介绍和使用

    MD5加密是最常用的加密方法之一,是从一段字符串中通过相应特征生成一段32位的数字字母混合码。对输入信息生成唯一的128位散列值(32个字符)。这篇文...

    LYSNote5432021-02-04
  • IOSiOS中滑动控制屏幕亮度和系统音量(附加AVAudioPlayer基本用法和Masonry简单使用)

    iOS中滑动控制屏幕亮度和系统音量(附加AVAudioPlayer基本用法和

    这篇文章主要介绍了iOS中滑动控制屏幕亮度和系统音量(附加AVAudioPlayer基本用法和Masonry简单使用)的相关资料,需要的朋友可以参考下...

    CodingFire13652021-02-26
  • IOSiOS中UILabel实现长按复制功能实例代码

    iOS中UILabel实现长按复制功能实例代码

    在iOS开发过程中,有时候会用到UILabel展示的内容,那么就设计到点击UILabel复制它上面展示的内容的功能,也就是Label长按复制功能,下面这篇文章主要给大...

    devilx12792021-04-02
  • IOSiOS开发之视图切换

    iOS开发之视图切换

    在iOS开发中视图的切换是很频繁的,独立的视图应用在实际开发过程中并不常见,除非你的应用足够简单。在iOS开发中常用的视图切换有三种,今天我们将...

    执着丶执念5282021-01-16
  • IOS详解iOS中多个网络请求的同步问题总结

    详解iOS中多个网络请求的同步问题总结

    这篇文章主要介绍了详解iOS中多个网络请求的同步问题总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    liang199111312021-03-15
  • IOSiOS实现控制屏幕常亮不变暗的方法示例

    iOS实现控制屏幕常亮不变暗的方法示例

    最近在工作中遇到了要将iOS屏幕保持常亮的需求,所以下面这篇文章主要给大家介绍了关于利用iOS如何实现控制屏幕常亮不变暗的方法,文中给出了详细的...

    随风13332021-04-02
  • IOSiOS开发技巧之状态栏字体颜色的设置方法

    iOS开发技巧之状态栏字体颜色的设置方法

    有时候我们需要根据不同的背景修改状态栏字体的颜色,下面这篇文章主要给大家介绍了关于iOS开发技巧之状态栏字体颜色的设置方法,文中通过示例代码...

    梦想家-mxj8922021-05-10
  • IOSiOS自定义UICollectionViewFlowLayout实现图片浏览效果

    iOS自定义UICollectionViewFlowLayout实现图片浏览效果

    这篇文章主要介绍了iOS自定义UICollectionViewFlowLayout实现图片浏览效果的相关资料,需要的朋友可以参考下...

    jiangamh8882021-01-11