一、在平時(shí)項(xiàng)目開(kāi)發(fā)中,經(jīng)常會(huì)使用WKWebView加載一個(gè)H5鏈接,加載完成后,用戶可能去玩其他的App,或者是接聽(tīng)電話,再?gòu)暮笈_(tái)回到App,有時(shí)候會(huì)發(fā)現(xiàn)WKWebView加載的H5變成白屏了,這樣展示給用戶非常的不友好。
二、造成H5白屏的原因:
- 網(wǎng)絡(luò)加載獲取數(shù)據(jù)造成
- 系統(tǒng)軟件版本造成
-
JS語(yǔ)言兼容造成 - 內(nèi)存不足造成
-
WKWebView的content process進(jìn)程被回收kill等
三、解決方案:
- 網(wǎng)絡(luò)不好情況數(shù)據(jù)加載失敗:
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
在這個(gè)方案里面添加失敗處理,可以提那家一個(gè)重新加載的按鈕圖片等。
- 對(duì)于
WKWebView的content process進(jìn)程被回收的處理,我們可以抓取屏幕的像素點(diǎn),白色的像素點(diǎn)占某個(gè)區(qū)域的多少值視為白屏,下面直接上代碼,白色像素點(diǎn)數(shù)占webview超過(guò)85%,視為白屏,WKWebView在iOS11之后提供截圖方法:
- (void)takeSnapshotWithConfiguration:(nullable WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void (^)(UIImage * _Nullable snapshotImage, NSError * _Nullable error))completionHandler WK_SWIFT_ASYNC_NAME(takeSnapshot(configuration:)) API_AVAILABLE(ios(11.0));
-
利用這個(gè)進(jìn)行處理白屏,代碼如下:
//聲明一個(gè)webview加載狀態(tài)枚舉
typedef NS_ENUM(NSUInteger,webviewLoadingStatus) {
WebViewNormalStatus = 0, //正常
WebViewErrorStatus, //白屏
WebViewPendStatus, //待決
};
-
判斷是否白屏方法封裝:
#pragma mark -- 判斷是否白屏方法封裝
- (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; //狀態(tài)欄高度
CGFloat navigationHeight = webview.viewController.navigationController.navigationBar.frame.size.height; //導(dǎo)航欄高度
WKSnapshotConfiguration *shotConfiguration = [[WKSnapshotConfiguration alloc] init];
shotConfiguration.rect = CGRectMake(0, statusBarHeight + navigationHeight, webview.bounds.size.width, (webview.bounds.size.height - navigationHeight - statusBarHeight)); //僅截圖檢測(cè)導(dǎo)航欄以下部分內(nèi)容
[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);
}
}];
}
}
}
-
圖片縮放
//縮放圖片
- (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;
}
}
-
遍歷像素點(diǎn) 白色像素占比大于95%認(rèn)定為白屏
- (BOOL)searchEveryPixel:(UIImage *)image {
CGImageRef cgImage = [image CGImage];
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
size_t bytesPerRow = CGImageGetBytesPerRow(cgImage); //每個(gè)像素點(diǎn)包含r g b a 四個(gè)字節(jié)
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āng)前像素點(diǎn)數(shù):%d,白色像素點(diǎn)數(shù):%d , 占比: %f",totalCount , whiteCount , proportion );
if (proportion > 0.85) {
return YES;
}else{
return NO;
}
}
-
在后臺(tái)回到前臺(tái)發(fā)送通知,執(zhí)行下面方法進(jìn)行調(diào)傭:
[self judgeLoadingStatus:self.webView withBlock:^(webviewLoadingStatus status) {
if (status == WebViewErrorStatus) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
JKAlertDialog *alertVB = [[JKAlertDialog alloc] initWithTitle:@"溫馨提示" message:@"內(nèi)存不足或占用過(guò)大,資源被回收,是否重新加載"];
[alertVB addButton:Button_OK withTitle:@"取消" handler:^(JKAlertDialogItem *item) {
}];
[alertVB addButton:Button_OK withTitle:@"重新加載" handler:^(JKAlertDialogItem *item) {
//重新加載webview
[self rest_requestLoad];
}];
[alertVB show];
});
}
}];