Mobile App Development

iOS App Development with DTCoreText: Rendering HTML as App Content

If you're developing an API-driven app, there is a good chance you'll be dealing with basic HTML content at some point. Be it rendering a user's bio from their profile, or the description of a local restaurant from a review site, HTML is the simplest and most common way to store and deliver rich text.

In the world of iOS development this is often accomplished by shoving the HTML content into a UIWebView for easy display, but this is inelegant. A UIWebView is really made to display web pages, not that other use is necessarily discouraged, but the documentation makes it clear the intent is to display navigable web pages and not to render HTML content as in-app content:

You use the UIWebView class to embed web content in your application. To do so, you simply create a UIWebView object, attach it to a window, and send it a request to load web content. You can also use this class to move back and forward in the history of webpages, and you can even set some web content properties programmatically.

A UIWebView can be adapted to look and feel like a non-web UI element, but instead of hacking that together, why not use a tool made for the job?

Enter DTAttributedTextView

I'm not going to give a background on DTCoreText, you can read up on that on the GitHub page. I'm also not going to cover installation, the GitHub page does that fine (though you may find CocoaPods simpler than the submodule approach).

Our focus here is the DTAttributedTextContentView, which is described as follows:

Attributed Text Content Views display attributed strings generated by DTHTMLAttributedStringBuilder. They can display images and hyperlinks inline or optionally place custom subviews (which get provided via the delegate in the appropriate places. By itself content views do not scroll, for that there is the UIScrollView subclass DTAttributedTextView.

In short, the DTAttributedTextView is a view for displaying rich text.

The Code

You can find a sample project on GitHub, which uses the same HTML content in a DTAttributedTextView, a UITextView, and a UIWebView, with code to make them all appear as similar as possible. To keep this post from getting out of hand I'm just going to go over the basic steps for using the DTAttributedTextView.

Typically this information would come from an API, but in our example we are going to work with a basic HTML file stored in the App bundle as content.html. This is retreived (in our viewDidLoad) as such:

NSString* filePath = [[NSBundle mainBundle] pathForResource:@"content" ofType:@"html"];
NSString *htmlString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

There are a few ways to convert the string, but we'll use a DTHTMLAttributedStringBuilder to create our AttributedString. By default, the builder creates Times New Roman text, to match a typical iOS UI we are changing this to Helvetica:

// Set our builder to use the default native font face and size
NSDictionary *builderOptions = @{
                                 DTDefaultFontFamily: @"Helvetica"
                                 };

The full list of options is available in the NSAttributedString(HTML) category documentation.

Next we just need to create a builder to generate our attributed string:

DTHTMLAttributedStringBuilder *stringBuilder = [[DTHTMLAttributedStringBuilder alloc] initWithHTML:htmlData
                                                                                           options:builderOptions
                                                                                documentAttributes:nil];

self.textView.attributedString = [stringBuilder generatedAttributedString];

In the event that you need to handle links you can provide a custom DTLinkButton class via the DTAttributedTextContentViewDelegate method. We're handling this at the end of our viewDidLoad, along with a tweak to the edge inset to keep the text from touching the edge of the view:

// Assign our delegate, this is required to handle link events
self.textView.textDelegate = self;

// Purely aesthetic, without this the text goes right up to the edge
self.textView.contentInset = UIEdgeInsetsMake(6, 8, 8, 8);

Setting up the delegate is fairly simple, you create a subclass of DTLinkButton to display the text and assign a target for the UIControlEventTouchUpInside event to handle links:

#pragma mark - DTAttributedTextContentViewDelegate

- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView
                          viewForLink:(NSURL *)url
                           identifier:(NSString *)identifier
                                frame:(CGRect)frame
{
    DTLinkButton *linkButton = [[DTLinkButton alloc] initWithFrame:frame];
    linkButton.URL = url;
    [linkButton addTarget:self
                   action:@selector(linkButtonClicked:)
         forControlEvents:UIControlEventTouchUpInside];

    return linkButton;
}

#pragma mark - Events

- (IBAction)linkButtonClicked:(DTLinkButton *)sender
{
    [[UIApplication sharedApplication] openURL:sender.URL];
}

And there we have it, HTML renderd cleanly in our app, without resorting to abusing a UIWebView.

A Note on Typography

DTCoreText renders type slightly differently than UIWebView or UITextView. This is very similar to WebKit's optimizeLegibility, though there are still slight rendering differences.

DTCoreText rendering is shown, hover over the image to see UIWebView rendering.

iOS Development with DTCore Text: Rendering HTML as App Content

iOS Development with DTCore Text: Rendering HTML as App Content

  • In Vestibulum the e tucked under the top of the V.
  • In the second sentence, Aenean is closer to the preceding period.
  • The link underline is slightly lower.

DTCoreText Caveats

There are two points you should consider before choosing to incorporate DTCoreText into your project:

These are both minor points, but they should be addressed all the same. That said, please refer to the DTCoreText project page for the most up-to-date information.

__

You've successfully subscribed to SmartLogic Blog
Great! Next, complete checkout for full access to SmartLogic Blog
Welcome back! You've successfully signed in.
Unable to sign you in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.