Display a page of HTML in a UIWebView

A UIWebView is like a browser. See Web Views in the iOS Human Interface Guidelines.

The UIWebView in this app displays the page
http://oit2.scps.nyu.edu/~meretzkm/INFO1-CE9236/src/html/preview.html
But a UIWebView is a browser with no back button or place to type a URL. If you tap on the links to www.filmratings.com or www.mpaa.org, there will be no way to get back to the green page (called a “green band” in the film industry).

Source code in Html.zip

  1. Class AppDelegate: unchanged.
  2. Class ViewController: added code to viewDidLoad.
  3. preview.html (not used until the exercise where we call the init(contentsOfFile:encoding:error) method of class String)

Create the project

Tell the view controller that the class of the view it should create is UIWebView. Select the Main.storyboard file in the Xcode Project Explorer. Along the top of the center panel, select

Html > Html > Main.storyboard > Main.storyboard (base) > View Controller Scene > View Controller > View

In the right panel of Xcode, click on the Identity Inspector icon. It’s a rectangle with a smaller rectangle in its upper left corner.
Custom Class
Class: UIWebView
Module: (leave it blank)

To add the text file preview.html to the project, follow the instructions in exercise 5 in States.

To launch in landscape orientation, select the project at the top of the Xcode Project Explorer. In the center panel of Xcode, scroll down to
Deployment Info → Device Orientation
Uncheck all the orientations except Landscape Left.

Three sources from which to get the page of HTML

  1. Download it from the web. The following code in the loadView method of class ViewController downloaded a file of HTML from the web into the UIWebView named webView.
    		let url: NSURL? = NSURL(string: "http://oit2.scps.nyu.edu/~meretzkm/INFO1-CE9236/src/html/preview.html");
    		if url == nil {
    			print("could not create url");
    			return;
    		}
    
    		let request: NSURLRequest = NSURLRequest(URL: url!);
    		webView.loadRequest(request);
    
    		//Begin with the green band faded out.
    		webView.alpha = 0;
    
    		//Fade in the green band.
    		UIView.animateWithDuration(2,
    			delay: 1,
    			options: UIViewAnimationOptions.CurveEaseInOut,
    			animations: {
    				self.view.alpha = 1;
    			},
    			completion: nil);
    
  2. Get the HTML from a file in the app’s bundle. Replace the above code with the following. The file preview.html has already been added to the project. It is a plain text file.
    		let bundle: NSBundle = NSBundle.mainBundle();
    
    		let filename: String? =
    			bundle.pathForResource("preview", ofType: "html");
    
    		if filename == nil {
    			print("could not find file preview.html");
    			return;
    		}
    
    		var error: NSError?;
    		let html: String? = String(contentsOfFile: filename!,
    			encoding: NSUTF8StringEncoding,
    			error: &error);
    
    		if html == nil {
    			print("could not load file: \(error)");
    			return;
    		}
    
    		webView.loadHTMLString(html!, baseURL: nil);
    
  3. Hardcode the HTML into one of the .swift files of the app. Replace the above code with the following.
    		let html: String =
    			  "<HTML>"
    			+ "<HEAD>"
    			+ "<META NAME = \"viewport\" CONTENT = \"width = device-width\">"
    			+ "<STYLE TYPE = \"text/css\">"
    			+ "<!--"
    			+ "SPAN.big  {font-size: 125%; font-weight: bold; padding: .33em;}"
    			+ "A:link    {font-size:  75%; color: white; text-decoration: none;}"
    			+ "A:visited {font-size:  75%; color: white; text-decoration: none;}"
    			+ "-->"
    			+ "</STYLE>"
    			+ "</HEAD>"
    
    			+ "<BODY STYLE = \"background-color: green; margin: 0px;\">"
    
    			+ "<TABLE STYLE = \""
    			+ "\tcolor: white;"
    			+ "\tmargin: auto;"
    			+ "\tfont: medium/200% Helvetica, sans serif;"
    			+ "\ttext-shadow: black .2em .2em 0;"
    			+ "\">"
    
    			+ "<TR>"
    			+ "<TD COLSPAN = \"2\""
    			+ "STYLE = \"padding-top: 7em; padding-bottom: 7em; text-align: center;\">"
    			+ "THE FOLLOWING"
    			+ "<SPAN CLASS = \"big\">PREVIEW</SPAN>"
    			+ "HAS BEEN APPROVED FOR"
    			+ "<BR><SPAN CLASS = \"big\">ALL AUDIENCES</SPAN>"
    			+ "<BR>BY THE MOTION PICTURE ASSOCIATION OF AMERICA, INC."
    			+ "</TD>"
    			+ "</TR>"
    
    			+ "<TR>"
    			+ "<TD ALIGN = \"LEFT\">"
    			+ "<A HREF = \"http://www.filmratings.com/\">www.filmratings.com</A>"
    			+ "</TD>"
    			+ ""
    			+ "<TD ALIGN = \"RIGHT\">"
    			+ "<A HREF = \"http://www.mpaa.org/\">www.mpaa.org</A>"
    			+ "</TD>"
    			+ "</TR>"
    			+ "</TABLE>"
    			+ ""
    			+ "</BODY>"
    			+ "</HTML>";
    
    		webView.loadHTMLString(html, baseURL: nil);
    

Things to try

  1. After you have faded the green band in, fade it out. In the viewDidLoad method of class ViewController, change the completion: parameter nil to the following closure.
    			{(b: Bool) -> Void in
    				UIView.animateWithDuration(2,
    					delay: 2,
    					options: UIViewAnimationOptions.CurveEaseInOut,
    					animations: {
    						webView.alpha = 0;//fade back out
    					},
    					completion: nil
    				);
    			}
    
  2. [Visit Westhampton.] In the viewDidLoad method of class ViewController, change
    "http://oit2.scps.nyu.edu/~meretzkm/INFO1-CE9236/src/html/preview.html"
    to
    "http://oit2.scps.nyu.edu/~meretzkm/INFO1-CE9236/src/html/evacuation.html"
    or to
    "http://oit2.scps.nyu.edu/~meretzkm/swift/html/manhattan.html" (go back to portrait). The evacuation and manhattan examples contain JavaScript. See this Android app.

  3. In the viewDidLoad method of class ViewController, change preview.html to crawl.html. (Look at crawl.html with Safari. In Firefox, remove the -webkit- prefixes, and change 40° to 25°.) Comment out the fade-in; start with the default alpha level of 1.

  4. To put an IMG in the web page, put the image file into the Supporting Files folder and specify a base URL.
    		let bundle: NSBundle = NSBundle.mainBundle();
    		let path: String = bundle.bundlePath;
    		let baseURL: NSURL? = NSURL.fileURLWithPath(path);
    		if baseURL == nil {
    			print("could not create URL");
    			return;
    		}
    		webView.loadHTMLString(html, baseURL: baseURL!);
    

  5. Display a navigation bar with a Back button above the UIWebView. Add the following methods to class ViewController.
    	//Called from the application delegate.
    
    	override init(nibName: String?, bundle: NSBundle?) {
    		super.init(nibName: nibName, bundle: bundle);
    	}
    
    	//not called
    	required init(coder aDecoder: NSCoder) {
    		super.init(coder: aDecoder);
    	}
    
    	//We will no longer specify in Main.storyboard that the view controller
    	//will create a UIWebView, so we will have to create the UIWebView here.
    
    	override func loadView() {
    		view = UIWebView(frame: CGRectZero);
    	}
    
    Get rid of the fade in and fade out. Append the following code to viewDidLoad.
    		navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back",
    			style: UIBarButtonItemStyle.Plain,
    			target: view,
    			action: "goBack");
    
    In Main.storyboard, create a UINavigationController instead of a ViewController. Insert the following code immediately before the return statement in the application(_:didFinishLaunchingWithOptions:) method of the app delegate.
    		if window == nil {
    			print("window is nil");
    		} else if window!.rootViewController == nil {
    			print("window!.rootViewController is nil");
    		} else {
    			let navigationController: UINavigationController = window!.rootViewController! as! UINavigationController;
    			let viewController: ViewController = ViewController(nibName: nil, bundle: nil);
    			navigationController.pushViewController(viewController, animated: false);
    		}
    

    To prevent the two links from disappearing off the bottom of the screen, make the padding-bottom in the .html file smaller.

    Can you display the title (or at least the URL) of the current page in the navigation bar? Can you let the user type a URL into a UITextField?

  6. The Back button should be initially disabled, and enabled only when there is a page to go back to. Add the following statement immediately after creating the button in viewDidLoad.
    		navigationItem.leftBarButtonItem!.enabled = false;
    

    Let the view controller act as the delegate of the web view:

    class ViewController: UIViewController, UIWebViewDelegate {
    
    		//after let webView = view as! UIWebView;
    		webView.delegate = self;
    

    Add the following method to the delegate (which is also the view controller).

    	//Called when the UIWebView arrives at a new page.
    
    	func webViewDidFinishLoad(webView: UIWebView) {
    		if navigationItem.leftBarButtonItem != nil {
    			if webView.canGoBack {
    				navigationItem.leftBarButtonItem!.enabled = true;
    			} else {
    				navigationItem.leftBarButtonItem!.enabled = false;
    			}
    		}
    	}
    
    You can write it more simply like this:
    	func webViewDidFinishLoad(webView: UIWebView) {
    		if navigationItem.leftBarButtonItem != nil {
    			navigationItem.leftBarButtonItem!.enabled = webView.canGoBack;
    		}
    	}
    

  7. Add the following method to the UIWebViewDelegate.
    	func webView(webView: UIWebView, didFailLoadWithError error: NSError) {
    		print("web view failed to load page: \(error)");
    	}
    

  8. Orson downloaded a Wikipedia article from @"http://en.m.wikipedia.org/wiki/The_Third_Man", where the first m stands for “mobile”. Remove the m. and send a User-Agent HTTP header in the HTTP request instead.
    	NSURL *url = [NSURL URLWithString: @"http://en.wikipedia.org/wiki/The_Third_Man"];
    
    	NSMutableURLRequest *request =
    		[NSMutableURLRequest requestWithURL: url];
    
    	//Download the web page in mobile format.
    	[request setValue: @"Mozilla/5.0 (iPhone)"
    		forHTTPHeaderField: @"User-Agent"];
    
    	NSURLResponse *response;
    	NSError *error;
    	NSData *data = [NSURLConnection sendSynchronousRequest: request
    		returningResponse: &response error: &error];
    
    	if (data == nil) {
    		NSLog(@"could not load URL %@: %@", url, error);
    	} else {
    		[webView loadData: data MIMEType: @"text/html"
    			textEncodingName: @"NSUTF8StringEncoding" baseURL: url];
    	}
    
    

    Here are five examples of this header.

    1. When I point iPhone Safari on the Simulator at the URL http://oit2.scps.nyu.edu:3001/ (specifying TCP/IP port number 3001 instead of the default 80), here is what the Safari says to the web server:

      GET / HTTP/1.1
      Host: oit2.scps.nyu.edu:3001
      User-Agent: Mozilla/5.0 (iPhone Simulator; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
      Accept-Language: en-us
      Accept-Encoding: gzip, deflate
      Connection: keep-alive
      
      
    2. When I point iPhone Safari on an actual iPhone at the URL http://oit2.scps.nyu.edu:3001/ (specifying TCP/IP port number 3001 instead of the default 80), here is what the Safari says to the web server:

      GET / HTTP/1.1
      Host: oit2.scps.nyu.edu:3001
      User-Agent: Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_10 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8E600 Safari/6533.18.5
      Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
      Accept-Language: en-us
      Accept-Encoding: gzip, deflate
      Cookie: __utma=3868456.363797723.1320369206.1320369206.1320369206.1; __utmz=3868456.1320369206.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
      Connection: keep-alive
      
    3. When I point a NSData object at the URL http://oit2.scps.nyu.edu:3001/ here is what the NSData object says to the web server:
      GET / HTTP/1.1
      Host: oit2.scps.nyu.edu:3001
      User-Agent: Orson/1.0 CFNetwork/548.0.3 Darwin/10.8.0
      Accept: */*
      Accept-Language: en-us
      Accept-Encoding: gzip, deflate
      Connection: keep-alive
      
      
    4. When I point the web browser on my Android MB612 phone at the URL http://oit2.scps.nyu.edu:3001/ here is what the browser says to the web server:
      GET / HTTP/1.1
      Host: oit2.scps.nyu.edu:3001
      Accept-Encoding: gzip
      Accept-Language: en-US
      x-wap-profile: http://device.sprintpcs.com/Motorola/MOTOMB612/KRNSX41110.rdf
      User-Agent: Mozilla/5.0 (Linux; U; Android 2.2.2; en-us; MB612 Build/KRNS-X4-1.1.10) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
      Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
      Accept-Charset: utf-8, iso-8859-1, utf-16, *;q=0.7
      
      
    5. When I point the Safari on a desktop Mac at the URL http://oit2.scps.nyu.edu:3001/ here is what the browser says to the web server:
      GET / HTTP/1.1
      Host: oit2.scps.nyu.edu:3001
      User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
      Accept-Language: en-us
      Accept-Encoding: gzip, deflate
      Cookie: amlbcookie=724216000.36895.0000; AMAuthCookie=AQIC5wM2LY4SfcwPauQA9hv-LEaKnjfRHUzNtCk76c8jb6I.*AAJTSQACMDIAAlNLAAoxMjYyOTI3OTQwAAJTMQACMDY.*
      Connection: keep-alive