iOS 開發中盡管高性能、高刷新的 WKWebview 在混合開發中大放異彩表現優異,但加載網頁過程中出現異常白屏的現象卻仍然屢見不鮮,且現有的 api 協議處理捕捉不到這種異常 case,造成用戶無用等待體驗很差。
針對業務場景需求,實現加載白屏檢測。考慮采用字節跳動團隊提出的 webview 優化技術方案。在合適的加載時機對當前 webview 可視區域截圖,并對此快照進行像素點遍歷,如果非白屏顏色的像素點超過一定的閾值,認定其為非白屏,反之重新加載請求。
下面是實現代碼,對 WKWebView 的擴展
import UIKit
import WebKit
/// 檢查 Web 是否白屏
extension WKWebView {
enum WebLoadingStatus{
case normal
case error
}
func judgeLoadingStatus(webView: WKWebView?, completion: ((_ status: WebLoadingStatus) -> Void)?) {
if #available(iOS 11.0, *) {
guard let webView = webView else {
return
}
let window = UIApplication.shared.keyWindow
guard let window = window else {
return
}
let webViewRect = webView.convert(webView.bounds, to: window)
let shotConfiguration = WKSnapshotConfiguration()
shotConfiguration.rect = CGRect(x: webViewRect.origin.x, y: webViewRect.origin.y, width: webViewRect.size.width, height: webViewRect.size.height)
webView.takeSnapshot(with: shotConfiguration) { [weak self] (image, error) in
guard let self = self, let image = image else {
return
}
let scaleImage = self.scaleImage(image: image)
let isWhiteScreen = self.searchEveryPixel(image: scaleImage)
if isWhiteScreen {
completion?(.error)
} else {
completion?(.normal)
}
}
}
}
/// 遍歷像素點,判斷白色像素是否占比大于 95%
/// - Parameter image: 圖片
/// - Returns: 占比
private func searchEveryPixel(image: UIImage) -> Bool {
let cgImage = image.cgImage
let width: size_t = cgImage?.width ?? 0
let height: size_t = cgImage?.height ?? 0
let dataProvider = cgImage?.dataProvider
let data = dataProvider?.data
let buffer: UnsafePointer<UInt8> = CFDataGetBytePtr(data)
var whiteCount = 0
var totalCount = 0
for x in 0 ..< height {
for y in 0 ..< width {
let point = CGPoint(x: x, y: y)
let pixelInfo: Int = (width * Int(point.y)) + Int(point.x) * 4
let red = CGFloat(buffer[pixelInfo])
let green = CGFloat(buffer[pixelInfo + 1])
let blue = CGFloat(buffer[pixelInfo + 2])
if red == 255.0 && green == 255.0 && blue == 255.0 {
whiteCount += 1
}
totalCount += 1
}
}
let proportion: CGFloat = CGFloat(whiteCount / totalCount)
if proportion > 0.95 {
return true
} else {
return false
}
}
/// 縮放圖片
/// 為了提升檢測性能,考慮將快照縮放至1/5,減少像素點總數,從而加快遍歷速度
/// - Parameter image: 原始圖片
/// - Returns: 縮放后圖片
private func scaleImage(image: UIImage) -> UIImage {
let scale = 0.2
// 縮略圖的尺寸在原圖寬高 * 縮放系數后可能不是整數,在布置畫布重繪時默認向上取整,這就造成畫布比實際縮略圖大
// 造成不準確,所以此處選擇 向下取整
let newSize = CGSize(width: floor(image.size.width * scale), height: floor(image.size.height * scale))
if #available(iOS 13.0, *) {
let renderer = UIGraphicsImageRenderer(size: newSize)
renderer.image { rendererContext in
return image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
}
}
return image
}
}