NEW: Get your ticket for Hacking with Swift Live 2019! >>

< Previous: Parsing JSON using the Codable protocol   Next: Finishing touches: didFinishLaunchingWithOptions >

Rendering a petition: loadHTMLString

After all the JSON parsing, it's time for something easy: we need to create a detail view controller class so that it can draw the petition content in an attractive way.

The easiest way for rendering complex content from the web is nearly always to use a WKWebView, and we're going to use the same technique from project 4 to create a DetailViewController that contains a web view.

Go to the File menu and choose New > File, then choose iOS > Source > Cocoa Touch Class. Click Next, name it “DetailViewController”, make it a subclass of “UIViewController”, then click Next and Create.

Replace all the DetailViewController code with this:

import UIKit
import WebKit

class DetailViewController: UIViewController {
    var webView: WKWebView!
    var detailItem: Petition?

    override func loadView() {
        webView = WKWebView()
        view = webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

This is almost identical to the code from project 4, but you'll notice I've added a detailItem property that will contain our Petition instance.

That was the easy bit. The hard bit is that we can't just drop the petition text into the web view, because it will probably look tiny. Instead, we need to wrap it in some HTML, which is a whole other language with its own rules and its own complexities.

Now, this series isn't called "Hacking with HTML," so I don't intend to go into much detail here. However, I will say that the HTML we're going to use tells iOS that the page fits mobile devices, and that we want the font size to be 150% of the standard font size. All that HTML will be combined with the body value from our petition, then sent to the web view.

Place this in viewDidLoad(), directly beneath the call to super.viewDidLoad():

guard let detailItem = detailItem else { return }

let html = """
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style> body { font-size: 150%; } </style>
</head>
<body>
\(detailItem.body)
</body>
</html>
"""

webView.loadHTMLString(html, baseURL: nil)

That guard at the beginning unwraps detailItem into itself if it has a value, which makes sure we exit the method if for some reason we didn’t get any data passed into the detail view controller.

Note: It’s very common to unwrap variables using the same name, rather than create slight variations. In this case we could have used guard let unwrappedItem = detailItem, but that isn’t any better.

I've tried to make the HTML as clear as possible, but if you don't care for HTML don't worry about it. What matters is that there's a Swift string called html that contains everything needed to show the page, and that's passed in to the web view's loadHTMLString() method so that it gets loaded. This is different to the way we were loading HTML before, because we aren't using a website here, just some custom HTML.

That's it for the detail view controller, it really is that simple. However, we still need to connect it to the table view controller by implementing the didSelectRowAt method.

Previously we used the instantiateViewController() method to load a view controller from Main.storyboard, but in this project DetailViewController isn’t in the storyboard – it’s just a free-floating class. This makes didSelectRowAt easier, because it can load the class directly rather than loading the user interface from a storyboard.

So, add this new method to your ViewController class now:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let vc = DetailViewController()
    vc.detailItem = petitions[indexPath.row]
    navigationController?.pushViewController(vc, animated: true)
}

Go ahead and run the project now by pressing Cmd+R or clicking play, then tap on a row to see more detail about each petition. Some petitions don’t have detail text, but most do – try a few and see what you can find.

SPONSOR Tired of wasting time debugging your Swift app? Instabug’s SDK is here to help you minimize your debugging time by providing you with complete device details, network logs, and reproduction steps with every bug report. All data is attached automatically. It only takes a line of code to setup. Get started for free.

< Previous: Parsing JSON using the Codable protocol   Next: Finishing touches: didFinishLaunchingWithOptions >
MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Average rating: 4.8/5

Click here to visit the Hacking with Swift store >>