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 aUIWebView
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 subclassDTAttributedTextView
.
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.
- 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:
- No text selection : Text selection is a feature bundled into DTRichTextEditor, a component available for purchase from Cocoanetics (currently 500 euros for lifetime source access).
- Attribution License : Using DTCoreText in your application is free if you provide attribution, or you can donate 75 euros to Cocoanetics for a non-attribution license.
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.
__