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.
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:
- 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.
All files are should be placed in the same folder, so that reference file path will be available at run time.
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
How to Load a Url Link That Is Inside a Web View and Keep It in That Web View in Swift
Launching Viber App via Url Scheme on iOS
Dashed Line Border Around Uiview
Xcode Changes Unmodified Storyboard and Xib Files
When Should Translatesautoresizingmaskintoconstraints Be Set to True
How to Implement Two Inits with Same Content Without Code Duplication in Swift
Ios7 Status Bar Hide/Show on Select Controllers
Extra Padding Above Table View Headers in iOS 15
Cfbundleversion Must Be Higher Than Previous Version
Swiftui: Navigationdestinationlink Deprecated
Upload Files to Dropbox from iOS App with Swift
Handling Touch Event in Uilabel and Hooking It Up to an Ibaction
Simply Mask a Uiview with a Rectangle
Detect If App Is Running in Slide Over or Split View Mode in iOS 9
How to Set Cmake_C_Compiler and Cmake_Cxx_Compiler for Building Assimp for iOS