You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WKWebView加载异常:自定义字体/CSS与loadHTMLString使用问题

解决WKWebView加载修改后HTML时在线资源与本地字体共存的问题

这个矛盾确实挺头疼的——baseURL设本地Bundle就断了在线资源,设nil又拿不到本地字体。我给你两个靠谱的解决方案,你可以根据自己的需求选:

方案一:转换在线资源为绝对URL,保留本地资源路径

核心思路是把原HTML里所有相对路径的在线资源(比如/css/main.css、image.jpg)改成绝对URL指向原网站,这样baseURL设为Bundle时,WebView只会去本地找你自定义的CSS和字体,在线资源还是从原网站拉。

步骤:

  1. 下载HTML后处理资源路径
    推荐用HTML解析库(比如SwiftSoup)来批量修改所有资源的href/src属性,比正则更可靠。示例代码:

    import SwiftSoup
    
    func processHTML(_ html: String, originalBaseURL: URL) throws -> String {
        let doc = try SwiftSoup.parse(html)
        // 遍历所有带资源路径的标签
        let resourceElements = try doc.select("link[href], img[src], script[src]")
        
        for element in resourceElements {
            let attr = element.hasAttr("href") ? "href" : "src"
            let originalPath = try element.attr(attr)
            // 把相对路径转换成原网站的绝对URL
            if let absoluteURL = URL(string: originalPath, relativeTo: originalBaseURL) {
                try element.attr(attr, absoluteURL.absoluteString)
            }
        }
        
        // 在head里插入自定义CSS链接
        let customCSSLink = try ElementFactory.createElement("link")
        try customCSSLink.attr("rel", "stylesheet")
        try customCSSLink.attr("type", "text/css")
        try customCSSLink.attr("href", "styles.css") // 相对Bundle的路径
        try doc.head()?.appendChild(customCSSLink)
        
        return try doc.html()
    }
    
  2. 配置本地CSS和字体
    确保styles.css和字体文件(比如CustomFont.ttf)都添加到项目中,并且勾选了Target Membership。CSS里的字体路径用相对Bundle的写法:

    @font-face {
        font-family: 'CustomFont';
        src: url('CustomFont.ttf') format('truetype');
        font-weight: normal;
        font-style: normal;
    }
    
    /* 其他自定义样式 */
    body {
        font-family: 'CustomFont', sans-serif !important;
    }
    
  3. 加载处理后的HTML

    let originalURL = URL(string: "https://your-target-site.com")!
    URLSession.shared.dataTask(with: originalURL) { data, _, error in
        guard let data = data, let rawHTML = String(data: data, encoding: .utf8) else { return }
        
        do {
            let processedHTML = try processHTML(rawHTML, originalBaseURL: originalURL)
            DispatchQueue.main.async {
                self.webView.loadHTMLString(processedHTML, baseURL: Bundle.main.bundleURL)
            }
        } catch {
            print("HTML处理失败:\(error)")
        }
    }.resume()
    

方案二:用WKUserScript注入自定义样式(更简洁)

如果不需要修改HTML结构,只是要覆盖样式和加载本地字体,用WKUserScript直接把CSS注入到页面里是更省心的办法。baseURL设为原网站的URL,保证在线资源正常加载,而字体用本地的绝对file URL。

步骤:

  1. 构建包含字体的自定义CSS
    先获取本地字体的绝对URL,然后把它写到CSS的@font-face里:

    guard let fontURL = Bundle.main.url(forResource: "CustomFont", withExtension: "ttf") else {
        print("字体文件未找到")
        return
    }
    let fontURLString = fontURL.absoluteString
    
    let customCSS = """
    @font-face {
        font-family: 'CustomFont';
        src: url('\(fontURLString)') format('truetype');
    }
    /* 你的其他自定义样式 */
    h1 {
        font-family: 'CustomFont' !important;
        color: #2c3e50 !important;
    }
    """
    
  2. 配置WKWebView注入脚本

    let userScript = WKUserScript(
        source: customCSS,
        injectionTime: .atDocumentEnd, // 页面加载完再注入,确保覆盖原有样式
        forMainFrameOnly: true
    )
    
    let config = WKWebViewConfiguration()
    config.userContentController.addUserScript(userScript)
    
    // 初始化WebView
    webView = WKWebView(frame: view.bounds, configuration: config)
    view.addSubview(webView)
    
  3. 加载页面
    不管是加载原网页,还是加载下载后的HTML,baseURL都设为原网站的URL:

    // 直接加载原网页
    webView.load(URLRequest(url: originalURL))
    
    // 或者加载下载后的HTML
    webView.loadHTMLString(downloadedHTML, baseURL: originalURL)
    

注意事项

  • 不管用哪种方案,都要确保本地文件(CSS、字体)在Target Membership里被勾选,否则Bundle里不会包含这些文件。
  • 方案一的正则替换容易踩坑(比如匹配到字符串里的路径),所以优先用SwiftSoup这类HTML解析库。
  • 方案二里的!important是为了确保自定义样式能覆盖原页面的样式,根据实际情况调整。

内容的提问来源于stack exchange,提问作者Exprove

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:25:02