Optimized Rendering of Text with CoreText
Given a large body of text, how do you parse it into pages that can fit onto a 1024x768 iPad screen? Or even break it up into beautiful magazine-like columns?
At first, CoreText seemed like an intimidating framework given I was unfamiliar with its C syntax, so I tried the brute force way: iterate over each word until boundingRectWithSize said I couldn't fit any more words for my view's frame. In a collectionView cell, the performance was horrible, as you could imagine, so scrolling was jittery.
Next was to try CoreText. Initially, I followed the Core Text Programming Guide's Columnar Layout example, which uses CTFrameSetterRef to divide the pages. The implementation suggests subclassing UIView and overriding drawRect.
The result was much better. The parsing of the text happened instantaneously. But when profiled for memory allocations via Instruments, the app showed it was holding onto 12 mb of memory for each rendered page. Given an article could have a dozen pages rendered onto the scrollview, memory was quickly building up and soon enough, you'd experience an out-of-memory crash. I had to research more about CoreGraphics performance.
I came across a great article about Rendering Graphics in iOS (GPU vs. CPU), and an even greater follow-up by Apple's (former) UIKit developer, Andy Matuschak!
The most important lesson for me was that drawing a view with the CPU-based framework, CoreGraphics, presents the tradeoff of gaining higher performance for increased memory usage.
The final implementation uses CTFrameSetterRef to quickly divvy up the text into an array of CFRanges (converted into NSRanges), but now I render the the appropriate range of substring into a regular UILabel, only when I need it. The label now consumes 7mb of memory, down from 12mb, for a full screen of text on an iPad.