Interfacing with a UIWebView from a UIViewController
If you are into iOS development chances are that you have already discovered (or been told) that UIWebView
is a powerfully beast that can be used from more the displaying remote web content.
In past projects I’ve relied heavily on UIWebView
to display rich content format by using a local HTML file included in the main bundle (with external CSS and JS files also there). Doing so is straightforward, but sooner than later you might run into the issue of having to interface with what’s running inside the UIWebView
from Objective-C or vice-versa. Luckily both are easy to implement and add a extra layer of power to using UIWebView as a rich content display.
Calling JavaScript from Objective-C
This one is straightforward: UIWebView
provides the method -stringByEvaluatingJavaScriptFromString:
. You pass it some Javascript code and get back the result of having it executed in the web view. Easy as it sounds.
Calling Objective-C code from UIWebView
This one is trickier, as there is no direct way to do it. Here’s what I do:
UIWebViewDelegate
provides the method -(BOOL)webView:shouldStartLoadWithRequest:navigationType:
.
This method is triggered before a URL is about to load in the web view. The return value is “YES if the web view should begin loading content; otherwise, NO”.
So the trick to bridge interactions in a UIWebView
and Objective-C code is as follows:
- Use some links with a dummy URL in the code, like
nativeAction://doSomething
. - Detect that URL scheme in the
UIWebView
delegatewebView:shouldStartLoadWithRequest:navigationType:
method. - If it matches your predefined URL, execute some native code and return
NO
. - Otherwise, return
YES
so theUIWebView
loads as usual.
Putting it all together
Let’s put both things together with a small sample. Here’s how the app looks:
It’s a UIWebView
with a UIToolBar
that has two buttons, to increase and decrease the font size (similar to several online publications would do). The app works as follows:
- Tapping the
UIToolBar
buttons change the size of the content in theUIWebView
. - By tapping the
UIWebView
the app hides (or shows) theUIToolBar
(as is common in reading apps).
Here’s the HTML (with embedded Javascript1) that the UIWebView
loads:
<html>
<head>
<script language="javascript">
function resizeText(multiplier) {
if (document.body.style.fontSize == "") {
document.body.style.fontSize = "1.0em";
}
document.body.style.fontSize =
parseFloat(document.body.style.fontSize) +
(multiplier * 0.2) + "em";
}
function touchStart(event) {
sX = event.touches[0].clientX;
sY = event.touches[0].clientY;
}
function touchEnd(event) {
var parentNode = event.target.parentNode
if ( parentNode != null && parentNode.nodeName.toLowerCase() == "a" )
return;
if (event.changedTouches[0].clientX == sX &&
event.changedTouches[0].clientY == sY) {
window.location = "nativeAction://hideShow";
}
}
document.addEventListener("touchstart", touchStart, false);
document.addEventListener("touchend", touchEnd, false);
</script>
</head>
<body>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Proin at justo malesuada, accumsan justo non, dapibus ante.
Maecenas luctus at magna nec convallis. Suspendisse ac
purus molestie mauris porttitor viverra sed a risus. Aliquam
pellentesque gravida ante, eu mattis lectus.
</body>
</html>
And here’s the UIViewController
that is also the UIWebViewDelegate
:
//
// QDNViewController.m
// WebViewSample
//
// Created by Pablo Bendersky on 8/31/13.
// Copyright (c) 2013 Pablo Bendersky. All rights reserved.
//
#import "QDNViewController.h"
@interface QDNViewController ()
@property (nonatomic, strong) IBOutlet UIWebView *webView;
@property (nonatomic, strong) IBOutlet UIToolbar *toolBar;
- (IBAction)decreaseTextSize:(id)sender;
- (IBAction)increaseTextSize:(id)sender;
- (void)resizeTextAdding:(NSInteger)textSizeIncrement;
@end
@implementation QDNViewController
- (void)viewWillAppear:(BOOL)animated {
NSURL *localFileURL = [[NSBundle mainBundle] URLForResource:@"content"
withExtension:@"html"];
[self.webView loadHTMLString:
[NSString stringWithContentsOfURL:localFileURL
encoding:NSUTF8StringEncoding
error:NULL]
baseURL:nil];
}
- (IBAction)decreaseTextSize:(id)sender {
[self resizeTextAdding:-1];
}
- (IBAction)increaseTextSize:(id)sender {
[self resizeTextAdding:1];
}
- (void)resizeTextAdding:(NSInteger)textSizeIncrement {
NSString *jsString = [NSString stringWithFormat:@"resizeText(%d)", textSizeIncrement];
[self.webView stringByEvaluatingJavaScriptFromString:jsString];
}
#pragma mark - UIWebViewDelegate methods
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
if ([request.URL.scheme caseInsensitiveCompare:@"nativeAction"] == NSOrderedSame) {
[UIView animateWithDuration:0.3 animations:^{
self.toolBar.alpha = (1 - self.toolBar.alpha);
}];
}
return YES;
}
@end
You can grab the entire code from this Github repository.