HTML Rending in Swift with Dynamic UITableViewCells

August 9, 2015

I’m a firm believer that native UI is superior to the web view experience on mobile. However, if you want rich-text user generated content support across both web and native platforms a markup language rendered natively is definitely the way to go. Trying to support the full-HTML standard natively sounds like a nightmare, but if you allow a restricted subset of the tags the problem becomes more manageable. In this article I’m going to walk through what solutions to HTML rendering exist on iOS and how I’ve implemented it under the intensive re-use / responsive scenario of rendering it in a UITableViewCell.

Approaches to Rendering HTML in iOS

We can all imaging how writing our own HTML parsers using the attributed string API would a less than fun experience. Luckily there are a few frameworks available out there:

Taking a look at all of these approaches I found that the DTCoreText gave me responsiveness while saving me the time of writing a solution myself. Lets walk through implementing it!

Setting Up Your Table View

I’m not going to cover each step in setting up a UITableViewController and UITableViewCell, but you can see how I have everything configured in the github example. The main parts you need to remember are to:

Rendering HTML in the UITableViewCell

First thing to do is to define a configure function on the UITableViewCell where we will be implementing the rending code:

func configure(htmlText: String) {
     ...

The first thing you need to do is define your font and color via the options dictionary:

let color = UIColor.blueColor()
let fontSize: Float = 12
var options = [
   DTCoreTextStub.kDTCoreTextOptionKeyFontSize(): NSNumber(float: Float(fontSize)),
   DTCoreTextStub.kDTCoreTextOptionKeyFontName(): "HelveticaNeue",
   DTCoreTextStub.kDTCoreTextOptionKeyFontFamily(): "Helvetica Neue",
   DTCoreTextStub.kDTCoreTextOptionKeyUseiOS6Attributes(): NSNumber(bool: true),
   DTCoreTextStub.kDTCoreTextOptionKeyTextColor(): color]
}

Now that we’ve defined the text style we can tell DTCoreText to create our attributed string:

let attributedString = DTCoreTextStub.attributedStringWithHtml(block.text, options: options)

Next, I’ve found it useful to clean up some of the extraneous new-lines that DTCoreText adds to keep our text field tight around the text:

let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
let range = NSMakeRange(0, attributedString.length)

//Strip out the extraneous \n added by DTCoreText
mutableAttributedString.mutableString.replaceOccurrencesOfString("\n", withString: "", options: NSStringCompareOptions.CaseInsensitiveSearch, range: range)

Lastly, you can apply any additional formatting you wish such as line spacing:

let stringRange = NSMakeRange(0, attrString.length)
var style = NSMutableParagraphStyle()
tyle.lineSpacing = 3.5
mutableAttributedString.addAttribute(NSParagraphStyleAttributeName, value: style, range: stringRange)

Finally we can render it in our UITextView:

textView.attributedText = mutableAttributedString
Wait! My text is truncated to a single line!
Don’t worry, this is a simple configuration we need to do to UITextView so it doesn’t assume we want a constant bounds with scrollable content. Simply disable scrolling to get multiple lines:
 
textView.scrollEnabled = false
If your still having issues with layout responsiveness or your HTML changes the container drastically you might additionally want to force a re-layout:
textView.layoutManager.ensureLayoutForTextContainer(textView.textContainer)
textView.layoutIfNeeded()

Now you should have a snappy UITableView that renders HTML natively. Check out the full example project on GitHub.