web-dev-qa-db-ja.com

UIWebView / WKWebViewで読み込まれたHTMLにCSSを挿入します

HTMLコンテンツを取得してUIWebViewに表示することに成功しました。

ただし、外部CSSファイルを追加してコンテンツをカスタマイズしたい。テキストとフォントのサイズのみを変更できます。変更を行うために考えられるすべてのソリューションを試しましたが、機能しません-変更は表示されません。

以下は私のコードです

HTMLNode* body = [parser body];
HTMLNode* mainContentNode = [body  findChildWithAttribute:@"id" matchingName:@"main_content" allowPartial:NO];
NSString *pageContent = [NSString stringWithFormat:@"%@%@", cssString, contentHtml];
        [webView loadHTMLString:pageContent baseURL:[NSURL URLWithString:@"http://www.example.org"]];

-(void)webViewDidFinishLoad:(UIWebView *)webView1{
    int fontSize = 50;
    NSString *font = [[NSString alloc] initWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'", fontSize];
    NSString *fontString = [[NSString alloc] initWithFormat:@"document.getElementById('body').style.fontFamily=\"helvetica\""];

    [webView1 stringByEvaluatingJavaScriptFromString:fontString];
    [webView1 stringByEvaluatingJavaScriptFromString:font];
}

私の視点でCSSスタイルシートを取得するのを手伝ってください。

29
cole

次のようにできます:

_- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSString *cssString = @"body { font-family: Helvetica; font-size: 50px }"; // 1
    NSString *javascriptString = @"var style = document.createElement('style'); style.innerHTML = '%@'; document.head.appendChild(style)"; // 2
    NSString *javascriptWithCSSString = [NSString stringWithFormat:javascriptString, cssString]; // 3
    [webView stringByEvaluatingJavaScriptFromString:javascriptWithCSSString]; // 4
}
_

このコードの機能:

// 1:すべてのCSS宣言を含む文字列を定義します

// 2:新しい_<style>_ HTML DOM要素を作成し、それにCSS宣言を挿入するjavascript文字列を定義します。実際には、次のステップで挿入が行われますが、現在は_%@_プレースホルダーのみがあります。行が長くなりすぎるのを防ぐためにこれを行いましたが、ステップ2と3は一緒に実行できます。

// 3:2つの文字列を組み合わせます

// 4:UIWebViewでJavaScriptを実行します

これが機能するには、HTMLに_<head></head>_要素が必要です。

編集:

ローカルのCSSファイル(この場合は「styles.css」という名前)からCSS文字列をロードすることもできます。ステップ// 1を次のように置き換えます。

_NSString *path = [[NSBundle mainBundle] pathForResource:@"styles" ofType:@"css"];
NSString *cssString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
_

別のオプションとして、CSSファイルをロードする_<link>_要素を_<head>_に挿入するだけです:

_- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"styles" ofType:@"css"];
    NSString *javascriptString = @"var link = document.createElement('link'); link.href = '%@'; link.rel = 'stylesheet'; document.head.appendChild(link)";
    NSString *javascriptWithPathString = [NSString stringWithFormat:javascriptString, path];
    [webView stringByEvaluatingJavaScriptFromString:javascriptWithPathString];
}
_

このソリューションは、大きなCSSファイルに最適です。残念ながら、リモートHTMLファイルでは機能しません。これは、アプリにダウンロードしたHTMLにCSSを挿入する場合にのみ使用できます。

更新:WKWebView/Swift 3.x

WKWebViewのセキュリティ設定のため、WKWebViewを使用して_<link>_要素を挿入しても機能しません。

それでも、CSSを文字列として挿入できます。コード_//1_にCSS文字列を作成するか、ローカルファイル_//2_に配置します。 WKWebViewでは、WKNavigationDelegatewebView(_:didFinish:)メソッドでインジェクションを行う必要があることに注意してください。

_func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    insertCSSString(into: webView) // 1
    // OR
    insertContentsOfCSSFile(into: webView) // 2
}

func insertCSSString(into webView: WKWebView) {
    let cssString = "body { font-size: 50px; color: #f00 }"
    let jsString = "var style = document.createElement('style'); style.innerHTML = '\(cssString)'; document.head.appendChild(style);"
    webView.evaluateJavaScript(jsString, completionHandler: nil)
}

func insertContentsOfCSSFile(into webView: WKWebView) {
    guard let path = Bundle.main.path(forResource: "styles", ofType: "css") else { return }
    let cssString = try! String(contentsOfFile: path).trimmingCharacters(in: .whitespacesAndNewlines)
    let jsString = "var style = document.createElement('style'); style.innerHTML = '\(cssString)'; document.head.appendChild(style);"
    webView.evaluateJavaScript(jsString, completionHandler: nil)
}
_
53
joern

UIWebViewはiOS 12で非推奨になったため、WKWebViewについてのみ回答します。受け入れられた回答で説明されているように、CSSの読み込みを実装しました。問題は、CSSが適用されていないHTMLからCSSが適用されたHTMLに移行することが時々見られることでした。
より良いアプローチは、WKUserScriptを使用して次のようにCSSを挿入することです。

lazy var webView: WKWebView = {
    guard let path = Bundle.main.path(forResource: "style", ofType: "css") else {
        return WKWebView()
    }

    let cssString = try! String(contentsOfFile: path).components(separatedBy: .newlines).joined()
    let source = """
      var style = document.createElement('style');
      style.innerHTML = '\(cssString)';
      document.head.appendChild(style);
    """

    let userScript = WKUserScript(source: source,
                                  injectionTime: .atDocumentEnd,
                                  forMainFrameOnly: true)

    let userContentController = WKUserContentController()
    userContentController.addUserScript(userScript)

    let configuration = WKWebViewConfiguration()
    configuration.userContentController = userContentController

    let webView = WKWebView(frame: .zero,
                            configuration: configuration)
    return webView
}()

このアプローチの詳細については、こちらをご覧ください ブログ投稿

10
Amer Hukic

スタイルタグでCSSを適用する代わりに、リンクタグでCSSを適用することをお勧めします:

func insertContentsOfCSSFile2(into webView: WKWebView) {
    guard let path = Bundle.main.path(forResource: "resource", ofType: "css") else { return }                
    let csFile = "var head = document.getElementsByTagName('head')[0];var link = document.createElement('link'); link.rel = 'stylesheet';link.type = 'text/css';link.href = '\(path)';link.media = 'all';head.appendChild(link);"

    webView.evaluateJavaScript(csFile) {(result, error) in
        if let error = error {
            print(error)
        }
    }
}

私はそれをテストしました。それはうまく機能しています。

1

詳細については、@ joernの承認済みの回答を参照してください。奇妙なEdgeのケースに遭遇したので、この答えを追加しています。 class="dialog"を使用してdivにスタイルを追加するために必要な私の特定の使用例。何らかの理由で、.dialogおよびdivを使用したスタイリングは機能しませんでしたが、他のタイプのスタイリングは機能していました。最後に、以下を使用してdialogの幅を設定しました

let width = Int(webView.bounds.width)
let script = "document.getElementsByClassName(\"dialog\")[0].style.width = \"\(width)px\""
webView.evaluateJavaScript(script)
0
SuperGuyAbe