iOS WKWebview 白屏檢測(cè)實(shí)現(xiàn)

iOS 開(kāi)發(fā)中盡管高性能、高刷新的 WKWebview 在混合開(kāi)發(fā)中大放異彩表現(xiàn)優(yōu)異,但加載網(wǎng)頁(yè)過(guò)程中出現(xiàn)異常白屏的現(xiàn)象卻仍然屢見(jiàn)不鮮,且現(xiàn)有的 api 協(xié)議處理捕捉不到這種異常 case,造成用戶無(wú)用等待體驗(yàn)很差。
針對(duì)業(yè)務(wù)場(chǎng)景需求,實(shí)現(xiàn)加載白屏檢測(cè)。考慮采用字節(jié)跳動(dòng)團(tuán)隊(duì)提出的 webview 優(yōu)化技術(shù)方案。在合適的加載時(shí)機(jī)對(duì)當(dāng)前 webview 可視區(qū)域截圖,并對(duì)此快照進(jìn)行像素點(diǎn)遍歷,如果非白屏顏色的像素點(diǎn)超過(guò)一定的閾值,認(rèn)定其為非白屏,反之重新加載請(qǐng)求。

下面是實(shí)現(xiàn)代碼,對(duì) WKWebView 的擴(kuò)展

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)
                }
            }
        }
    }
    
    /// 遍歷像素點(diǎn),判斷白色像素是否占比大于 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
        }
    }
    
    /// 縮放圖片
    /// 為了提升檢測(cè)性能,考慮將快照縮放至1/5,減少像素點(diǎn)總數(shù),從而加快遍歷速度
    /// - Parameter image: 原始圖片
    /// - Returns: 縮放后圖片
    private func scaleImage(image: UIImage) -> UIImage {
        let scale = 0.2
        // 縮略圖的尺寸在原圖寬高 * 縮放系數(shù)后可能不是整數(shù),在布置畫(huà)布重繪時(shí)默認(rèn)向上取整,這就造成畫(huà)布比實(shí)際縮略圖大
        // 造成不準(zhǔn)確,所以此處選擇 向下取整
        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
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容