An example of white screen detection in IOS wkwebview

Time:2021-6-6

preface

    Since the introduction of wkwebview in IOS 8, it has greatly improved the loading speed and memory leakage of web pages, and gradually replaced the cumbersome UI WebView. Although wkwebview with high performance and high refresh performs well in hybrid development, it is still common to see abnormal white screen in the process of loading web pages, and the existing API protocol can not catch this abnormal case, resulting in poor user useless waiting experience.
    According to the requirements of business scenarios, the loading white screen detection is implemented. Consider using the WebView optimization technology proposed by the team. At the right time of loading, take a screenshot of the current WebView visual area, and traverse the pixels of this snapshot. If the pixels of non white screen color exceed a certain threshold, it is considered as non white screen, otherwise, reload the request.

Take Snapshot

IOS official provides a simple interface to get WebView snapshot, through asynchronous callback to get the screen capture of the current visual area.


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

The snapshot configuration parameter can be used to configure the snapshot size range and capture the entire screen area of the current client by default. Due to the special situation that the navigation bar is loaded successfully but the content page is blank, the number of non white screen pixels will increase, which will affect the final judgment result, so it is considered to eliminate it.

- (void)judgeLoadingStatus:(WKWebView *)webview {
  if (@available(iOS 11.0, *)) {
    if (webView && [webView isKindOfClass:[WKWebView class]]) {

      CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height; // Status bar height
      CGFloat navigationHeight = webView.viewController.navigationController.navigationBar.frame.size.height; // Navigation bar height
      WKSnapshotConfiguration *shotConfiguration = [[WKSnapshotConfiguration alloc] init];
      shotConfiguration.rect = CGRectMake(0, statusBarHeight + navigationHeight, _ webView.bounds.size.width, (_ webView.bounds.size.height - navigationHeight - statusBarHeight)); // Only the following parts of the navigation bar are detected in the screenshot
      [_webView takeSnapshotWithConfiguration:shotConfiguration completionHandler:^(UIImage * _Nullable snapshotImage, NSError * _Nullable error) {
        //todo
      }];
    }
  }
}

Zoom snapshot

In order to improve the detection performance, we consider scaling the snapshot to 1 / 5 to reduce the total number of pixels, so as to speed up the traversal speed.


- (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;
  }
}

Performance comparison before and after reduction (experimental environment: iPhone 11 on the same page)

White screen detection before zooming:

It takes 20ms

White screen detection after zooming:

It takes 13 Ms

Notice there’s a small hole here. Because the size of the thumbnail may not be an integer after the width and height of the original image * the scaling factor, it will be rounded up by default when the canvas is redrawn, which causes the canvas to be larger than the actual thumbnail. When traversing the thumbnail pixels, the pixels on the canvas outside the image will be taken into account, resulting in the actual white screen page pixel ratio is not 100%, as shown in the figure. So use floor to round the size down.

Traverse snapshot

Traverse the snapshot thumbnail pixels, and identify the page with white pixels (R: 255 G: 255 B: 255) accounting for more than 95% as white screen.

- (BOOL)searchEveryPixel:(UIImage *)image {
  CGImageRef cgImage = [image CGImage];
  size_t width = CGImageGetWidth(cgImage);
  size_t height = CGImageGetHeight(cgImage);
  size_ t bytesPerRow = CGImageGetBytesPerRow(cgImage); // Each pixel contains four bytes of RGBA
  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 (@ "current pixel number: D, white pixel number: D, proportion: F", totalcount, whitecount, proportion) ";
  if (proportion > 0.95) {
    return YES;
  }else{
    return NO;
  }
}

summary

typedef NS_ENUM(NSUInteger,webviewLoadingStatus) {

  Webviewnormalstatus = 0, // normal

  Webviewerrorstatus, // white screen

  Webviewpendstatus, // pending
};

//Judge whether the screen is white or not
- (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; // Status bar height
      CGFloat navigationHeight = webview.viewController.navigationController.navigationBar.frame.size.height; // Navigation bar height
      WKSnapshotConfiguration *shotConfiguration = [[WKSnapshotConfiguration alloc] init];
      shotConfiguration.rect = CGRectMake(0, statusBarHeight + navigationHeight, webview.bounds.size.width, (webview.bounds.size.height - navigationHeight - statusBarHeight)); // Only the following parts of the navigation bar are detected in the screenshot
      [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);
        }
      }];
    }
  }
}

//Traversal pixel white pixels accounted for more than 95% identified as white screen
- (BOOL)searchEveryPixel:(UIImage *)image {
  CGImageRef cgImage = [image CGImage];
  size_t width = CGImageGetWidth(cgImage);
  size_t height = CGImageGetHeight(cgImage);
  size_ t bytesPerRow = CGImageGetBytesPerRow(cgImage); // Each pixel contains four bytes of RGBA
  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 (@ "current pixel number: D, white pixel number: D, proportion: F", totalcount, whitecount, proportion) ";
  if (proportion > 0.95) {
    return YES;
  }else{
    return NO;
  }
}

//Zoom picture
- (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;
  }
}

It only needs to call back in the appropriate view life cycle to use this function method to detect whether the page state is white or not, and the performance loss is negligible.

statement

Author: bbtime
Link:https://juejin.im/post/6885298718174609415

The above is the detailed content of the implementation example of IOS wkwebview white screen detection. For more information about IOS wkwebview white screen detection, please pay attention to other related articles of developer!

Recommended Today

What is “hybrid cloud”?

In this paper, we define the concept of “hybrid cloud”, explain four different cloud deployment models of hybrid cloud, and deeply analyze the industrial trend of hybrid cloud through a series of data and charts. 01 introduction Hybrid cloud is a computing environment that integrates multiple platforms and data centers. Generally speaking, hybrid cloud is […]