Swift - Apply Local CSS to Web View

Swift - Apply local css to web view

Add delegate method of UIWebView like this

func webViewDidFinishLoad(webView: UIWebView) {    
if let path = Bundle.main.path(forResource: "styles", ofType: "css") {
let javaScriptStr = "var link = document.createElement('link'); link.href = '%@'; link.rel = 'stylesheet'; document.head.appendChild(link)"
let javaScripthPath = String(format: javaScriptStr, path)
self.articleView.stringByEvaluatingJavaScript(from: javaScripthPath)
}

//Second way
do {
let cssContent = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding as NSStringEncoding);
let javaScrStr = "var style = document.createElement('style'); style.innerHTML = '%@'; document.head.appendChild(style)"
let JavaScrWithCSS = NSString(format: javaScrStr, cssContent)
self.articleView.stringByEvaluatingJavaScriptFromString(JavaScrWithCSS as String)
} catch let error as NSError {
print(error);
}
}

Hope this will help you.

SWIFT - Loading local CSS file

Your HTML file is loaded from a string, so it's loaded into a web view that can't access files on the file system (probably due to the Same Origin Policy that browsers implement). It can only access resources similarly injected in via loadHTMLString.

If you want to use local CSS, load your HTML from a file instead of a string. This will give the web view access to the CSS if it's in the same directory.

Here is a demo, first, the HTML is now a file.

Sample Image

Then, your code can look like this:

class ViewController: UIViewController, UIWebViewDelegate {

@IBOutlet weak var webview: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
webview.delegate = self

let path = Bundle.main.path(forResource: "app", ofType: "html")!
let url = URL(fileURLWithPath: path)
let request = URLRequest(url: url)

webview.loadRequest(request)
}

func webViewDidFinishLoad(_ webView: UIWebView){
let path = Bundle.main.path(forResource: "styles", ofType: "css")!

let javaScriptStr = "var link = document.createElement('link'); link.href = '\(path)'; link.rel = 'stylesheet'; document.head.appendChild(link)"
webview.stringByEvaluatingJavaScript(from: javaScriptStr)
}
}

Load .CSS file from within app in uiwebview

You are basically on the right track, but you should be able to add the css using the same stringByEvaluatingJavaScriptString method.

- (void)webViewDidFinishLoad:(UIWebView *)webView {

NSString *jsFile = @"storage.js";
NSString *jsFilePath = [[NSBundle mainBundle] pathForResource:jsFile ofType:nil];
NSURL *jsURL = [NSURL fileURLWithPath:jsFilePath];
NSString *javascriptCode = [NSString stringWithContentsOfFile:jsURL.path encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:javascriptCode];

// Updated to load css from "styling.css" file.
NSString *cssFile = @"styling.css";
NSString *cssFilePath = [[NSBundle mainBundle] pathForResource:jsFile ofType:nil];
NSURL *cssURL = [NSURL fileURLWithPath:jsFilePath];
NSString *cssString = [NSString stringWithContentsOfFile:jsURL.path encoding:NSUTF8StringEncoding error:nil];

// NSString *cssString = @"body { font-family: Verdana, Geneva, sans-serif; font-size: 50px; color: #F00; }";

NSString *javascriptString = @"var style = document.createElement('style'); style.innerHTML = '%@'; document.head.appendChild(style)";
NSString *javascriptWithCSSString = [NSString stringWithFormat:javascriptString, cssString];
[webView stringByEvaluatingJavaScriptFromString:javascriptWithCSSString];

}

Once you do this, you can verify that the style is applied by using Safari remote debugging to view the source for the page displayed on the device / simulator. It can show you if some existing CSS is overriding yours.

load a local css file into a webview

The reason is CSS and JS files not available at runtime for render in HTML.

You need to take care of some points:

  1. All CSS/JS/HTML files must be added to Project Properties >> Build Phases >> Copy Bundle Resources.

Check that all files are added if not then manually add those files.

Sample Image


  1. All files are should be placed in the same folder, so that reference file path will be available at run time.

  2. Instead of using directly file names, I will prefer you to use marker. Use symbols as a marker to replace with actual path of CSS and JS files.

For example:

NSString *HTML_HEADER=@"<HTML><HEAD><link rel='stylesheet' href='#FILE1#' type='text/css'></HEAD><BODY>";
NSString *HTML_FOOTER=@"</BODY></HTML>";

NSString *cssFilePath = [[NSBundle mainBundle] pathForResource:@"style" ofType:@"css"];
NSString *html_header_with_files = [HTML_HEADER stringByReplacingOccurrencesOfString:@"#FILE1#" withString:cssFilePath];

NSString *htmlString = [NSString stringWithFormat:@"%@%@%@",html_header_with_files,notif.html,HTML_FOOTER];

NSLog(@"htmlString %@", htmlString);
[self.webView loadHTMLString:htmlString baseURL:nil];

In above example I have replace style.css string from html part and add marker #FILE1#.

This marker is relative url to file style.css and it will be replace before html load in webView.

So whenever you would like to load html to webView, load all resource files from their relatives urls.

Here HTML_HEADER will be replace with actual path for all resources files.

How can I load the CSS file with HTML in Swift 3?

Follow these four steps to load a CSS file in a WebView:

Step 1:
Access the local CSS file from the Bundle.

guard let path = Bundle.main.path(forResource: "Style", ofType: "css") else { return }

Step 2:
Get the CSS content in String format.

let cssString = try! String(contentsOfFile: path).trimmingCharacters(in: .whitespacesAndNewlines)

Step 3:
Write your JavaScript in a string to be evaluated later by the WebView.

let jsString = "var style = document.createElement('style'); style.innerHTML = '\(cssString)'; document.head.appendChild(style);"

Step 4: Render the JavaScript.

webView.evaluateJavaScript(jsString, completionHandler: nil)

DOCUMENTATION

  • Framework: WebKit
    https://developer.apple.com/documentation/webkit/
  • Class: WKWebView
    https://developer.apple.com/documentation/webkit/wkwebview
  • Instance Method: evaluateJavaScript(_:completionHandler:)
    https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript

Loading local css & js files into WKWebView

After a bit of reading here about the file url I was able to solve the problem.

Here is the code

import UIKit
import WebKit

class ViewController: UIViewController {
@IBOutlet var containerView: UIView! = nil //allows the class to reference WKWebView
var webView: WKWebView?

override func loadView() {
super.loadView()

self.webView = WKWebView()
self.view = self.webView!
}

override func viewDidLoad() {
super.viewDidLoad()

let baseUrl = NSURL(string: "file:///<yourFilePath>/abc/")
let path = NSBundle.mainBundle().pathForResource("abc/index", ofType: "html")
let HTMLString: NSString?

do {
HTMLString = try NSString(contentsOfFile: path!, encoding: NSUTF8StringEncoding)
webView!.loadHTMLString(HTMLString as! String, baseURL: baseUrl )

} catch {
HTMLString = nil
}
}
}

Insert CSS into loaded HTML in UIWebView / WKWebView

You can do it like this:

- (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
}

What this code does:

// 1 : Define a string that contains all the CSS declarations

// 2 : Define a javascript string that creates a new <style> HTML DOM element and inserts the CSS declarations into it. Actually the inserting is done in the next step, right now there is only the %@ placeholder. I did this to prevent the line from becoming too long, but step 2 and 3 could be done together.

// 3 : Combine the 2 strings

// 4 : Execute the javascript in the UIWebView

For this to work, your HTML has to have a <head></head> element.

EDIT:

You can also load the css string from a local css file (named "styles.css" in this case). Just replace step //1 with the following:

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

As another option you can just inject a <link> element to the <head> that loads the CSS file:

- (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];
}

This solution works best for large CSS files. Unfortunately it does not work with remote HTML files. You can only use this when you want to insert CSS into HTML that you have downloaded to your app.

UPDATE: WKWebView / Swift 3.x

When you are working with a WKWebView injecting a <link> element does not work because of WKWebView's security settings.

You can still inject the css as a string. Either create the CSS string in your code //1 or put it in a local file //2. Just be aware that with WKWebView you have to do the injection in WKNavigationDelegate's webView(_:didFinish:) method:

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)
}

Load local resources along with HTML string in WKwebView (SWIFT)?

Click on "use folder reference" option instead of "create groups" while adding the resources files and folders into bundle.



Related Topics



Leave a reply



Submit