Almost always when we iOS developers want to display some rich text we are using UIWebView. That’s not by choice, since traditionally Apple did not provide any classes to us being able to show formatted text.
That changed slightly with the iPad, because in 3.2 we got CoreText as well as CATextLayer. CoreText gives us NSAttributedString which is basically a string that can have different attributes for ranges of characters. Those attributes can either be standard ones, like to describe the font, color, size and paragraph format. Or they can be your own arbitrary attributes.
I’ve shown how to programmatically construct these in my previous article on CoreText. The one thing though that is still missing from making CoreText really useful are ways to create attributed strings. Clearly doing it all in code is not feasable.
In this article I am introducing an Open Source project that aims to provide the missing functionality to iOS developers.
UIWebView is a wrapper class around Webkit and unfortunately Apple does not want to give us more control over it. Possibly so that nobody goes and breaks it. These are only some of the drawbacks:
- You cannot properly control generation of thumbnails or bitmaps
- Webkit is not threadsafe (according to Apple documentation), so you risk blocking your main thread and UI if you render graphics with it
- No (official) control over the shade shown around the content, like removing it if it does not work well in your app style.
- It’s slow compared to regular drawing, even a local HTML document first shows a blank screen until it appears
- if you ditch the rich and just keep the text, then UITextView seems like an option, but you cannot affect the styling of (detected) hyperlinks and each such links exits your app
While there are certain unofficial workarounds the general situation is unbearable if you want to use rich text. Use WebView or render your about screens as PNGs, adding all the additional overhead of having to redo everything if you’re just changing like a version number of the app.
Looking at NSAttributedString.h on the Mac, you find some very interesting methods, commented by Apple as “The following methods should now be considered as conveniences for various common document types.”
- - (id)initWithRTF:(NSData *)data documentAttributes:(NSDictionary **)dict;
- - (id)initWithRTFD:(NSData *)data documentAttributes:(NSDictionary **)dict;
- - (id)initWithHTML:(NSData *)data documentAttributes:(NSDictionary **)dict;
- - (id)initWithHTML:(NSData *)data baseURL:(NSURL *)base documentAttributes:(NSDictionary **)dict;
- - (id)initWithDocFormat:(NSData *)data documentAttributes:(NSDictionary **)dict;
- - (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary **)dict;
And convenient they ARE, just not to us. Let this sink in a bit: those Mac types have methods to read RTF, Word Doc and HTML files into NSAttributedStrings. It’s SO UNFAIR.
We can never hope to duplicate all the richness and features of Webkit, BUT my tests have show that if you have control over the quality of the HTML code – like if it’s your own for a credits screen – then it is absolutely feasable to provide a category on NSAttributedString that generates these from your HTML.
For this exact purpose I started an Open Source project on GitHub: NSAttributedString-Additions-for-HTML.
Now I have an inkling that Apple will eventually port the above mentioned “convenient methods” to iOS as well, maybe as soon as in SDK 5. But when do you think will the majority of your customers have iOS 5 on their devices? If the adoption rate of iOS 4 is any indication then this will take until spring 2012. Using our code literally gives you a one year head start.
Also you have an option of falling back to UIWebView on devices running an iOS before 3.2 and falling forward to these official methods – if they ever come – if you find them to be available on the users iOS 5 device.
Our initWithHTML methods aim to perfectly match the output from the Mac version. This is possible to achieve for characters and I have unit tests in place that make certain this keeps being perfect. Regarding the attributes there are many things that have to be done slightly different on iOS to achieve the same look. I won’t bother you with the details there.
But this is only the first part of the story. You might have thought that all you need is to get these pesky NSAttributedStrings and then CATextLayer will do the rest. ‘fraid not. CATextLayer does not obey paragraph formatting and it does not deal with images and link interactivity. This might change in future versions, but so far CATextLayer is only good if you want to display static text on a button or label.
This is why we are also developing DTAttributedTextView and DTAttributedTextContentView. The first being a scrollview subclass for longer text and the latter being responsible for the drawing itself. This is this the perfect replacement for UITextView or UIWebView.
Another level of improving over what web views give you is the way of customization you can do. DTAttributedTextContentView asks a delegate for a custom UIView for each glyph run on screen, providing the coordinates for it. This way you can add interactivity or custom UIViews to your HTML as you could never do before. An example shows adding a movie player for a HTML5 video tag. Many more options are possible, like adding custom rendering for SVG images.
This project wants to give you an option to NOT use UIWebView where you don’t actually need the whole browsing experience, but just rich text and hyperlinks.
You have two ways how you can get the source: you can either download a ZIP/TAR or you can clone the git repo. The latter option allows you to get the updates that get added now on a daily basis.
Development on this component continues are more an more people implement it into their iOS apps. If you find some HTML that does not come out right, then please send it to us for inspection so that we can add or tweak the HTML parser to deal with it properly. Let’s call that “purpose-driven development”.
My long term goal for this is actually something even more ambitious: a Rich-Text editor. I am envisioning creating a component that allows you to edit NSAttributedStrings, copy/paste and generate HTML from it. Maybe Apple will finally give us something like this in iOS 5, keeping our fingers crossed. I have the theory that the rich text editing in the iOS apps are a taste of the things to come to iOS 5. But if this fails to materialize then I will endeavor to fill this void.
A US-based company has expressed interest in sponsoring development of additional needed features for displaying digital documents. Add this monetary incentive to the fact that I am now spending mornings adding new features and fixing stuff you will find that this project is growing in leaps and bounds.
As any FOSS initiative it also depends on people making modifications, improvements and fixing bugs to then let me pull these changes into my master repository. So please have a look at the project and begin to replace web views where possible. The snags you might hit there are invaluable feedback to us to know what we still need to work on.