I made some updates recently that I wanted to mention so as to minimize some surprises. Also there are some changes that were prompted by iOS 6 being released.
The component is for sale by itself or as part of DTRichTextEditor. It is a perfect copy of the 3 magnifying glasses that are part of iOS including the showing and hiding animations. Since Apple still does not give access to the native loupe this component still has a reason to live.
Yesterday I did some major reworking due to a crash that started to occur on iOS 6. When presenting the loupe the developer specifies a target view. I would walk up the view hierarchy until I reached the UIWindow and then moved back down one level to find the root view. UIWindows don’t get rotated when the device is rotated, but the first view does which made this the perfect spot to mount the loupe view on.
There is a bug/feature in iOS 6 that a modally presented view controller does not like if a subview is added higher up in the view hierarchy than the internal views that are used to display the shadow of the modal view. Strangely this would result in a crash on [CALayer layer] in Apple’s code. Probably a bug, but I cannot be certain because I was doing a nasty hack anyway.
The loupe contents comes from a renderLayerInContext and to avoid the loupe seeing itself I was briefly hiding it, rendering the layers and then immediately showing it again. Another nasty hack.
To work around the problem I changed the strategy such to give the loupe its own UIWindow. Now when the loupe is being presented I am synching the rotation and bounds of the loupe special window with the root view. This extra window has the additional benefit that I don’t need the hide/show hack any more as well.
While I was at it I also made the loupe window and instance a singleton. Now you don’t call alloc/init any more on the loupe but retrieve the singleton via [DTLoupeView sharedLoupe]. To be honest the previous implementation sucked, because it would cause loupes to pile up over time. The new one doesn’t have this effect.
This project is the repository for all the kinds of helper methods that I am using myself all the time. It also has become home to several key classes that I want a central place to maintain them at.
I found some categories on classes that exist both on iOS and Mac very useful, but the previous monolithic static library several annoying drawbacks. It mixed stuff that is platform-independent with categories on UIKit views. So I started a solo target that allows me to include the pieces that also work on Mac via Mac library.
The other annoying thing was that some components in DTFoundation would have dependencies. DTHTMLParser for example would require libxml2. If you included DTFoundation then the linker would force you to also link in libxml2, even if you didn’t do any HTML parsing in your project. The same is true for DTZipArchive which requires zlib and DTDownloadCache which requires CoreData.
So I decided to split these out of DTFoundation and provide static library targets for these by themselves. The goal being that if you included DTFoundation you would never have to add additional dependencies that you don’t need.
I want to add a podspec for DTFoundation as well and there it also makes sense to have specs for the individual parts that have dependencies independent from those which don’t.
Something that was bugging me for quite some time was the fact that DTCoreText is using two classes that had found their home in my DTFoundation project: DTVersion and DTHTMLParser. I had to maintain a duplicate version of these inside of DTCoreText. I recently fixed a bug there when encountering a processing instruction and of course I had to copy this over into DTCoreText as well.
So I removed the duplicate classes and instead added DTFoundation as git submodule. DTHTMLParser is linked in via the new static library containing only it. DTVersion is universal and comes from the standard DTFoundation static library. Both of these are merged into the DTCoreText static library. For developers who also use DTFoundation directly in their projects there is a static library target for DTCoreText that does not include the DTFoundation stuff.
As more and more people seem to want to use CocoaPods to include other developer’s components into their projects I was nudged to think how to also resolve the duplication there. If I’m referencing DTFoundation and DTHTMLParser from one git submodule then there should be a method to model that via podspecs as well. Though here I still need to do a bit of research. Is it possible to create one podspec that works for both Mac and iOS and also have sub-specs that mirror the 3 new sub-libraries?
The 1.0.2 tag on the DTCoreText project is the last one that does not have the dependency on DTFoundation.
There is one more change of note in DTCoreText, related to my rich text editor component. Previously any HTML tag would inherit a plethora of attributes from its parent. If you copy a snippet from mobile Safari then it is encoded as a web archive on the pasteboard. It looks like iOS 6 is doing a more complete job there than it did previously.
One client of mine reported that if you copied a table that had a gray background color under iOS 6 the text pasted into DTRichTextEditor would now also get a gray background. The reason of course being the mentioned “brute force” inheritance. I modified DTCoreText to only inherit background-color from an inline element. A block-level element like div or table would be in charge of drawing the entire box in the stated color and thus it would not make sense to pass it on to contained inline-level elements.
Since I have the master repo for DTRichTextEditor on my Subversion server I need to have a repo-local mirror of DTRichText inside it. Again a form of duplication that bothers me for a long time now. Because of this I am considering to switch to git as soon as my Linux guru can set up a git server for me. I also thought about paying for a private GitHub repo, but I don’t feel comfortable having my most valuable code hosted by a third party.
And here the circle closes, because DTRichTextEditor is also a heavy user of DTLoupeView mentioned at the beginning of this article and also got an update there via svn sub-module. So if you are a user of that please make sure that you recursively update all the projects in there.
Closing Thoughts: DTCoreText versus iOS 6
iOS 6 added a few object-based attributes to CoreText and also the capability to display attributed strings using these new attributes in several UIKit classes, most importantly UITextView. This works fine for the most part unless…
- you have projects that still need to support earlier iOS versions
- you want to embed images, video or custom objects in the text
- you have HTML as the stuff to generated attributed strings out of
A few months ago i had to rename the initWithHTML methods to initWithHTMLData because linking against the iOS 6 BETA SDKs would cause a duplicate symbol problem. This tells us that the initWithHTML is actually present in the OS now, as it has been for a while on Mac. But at present it is a private method and not app-store legal. (I filed a Radar for that: rdar://11664604, closed as duplicate of rdar://11689785)
I get the impression that Apple engineers where rushing to get rich text support into UIKit for iOS 6, but had to leave out several items to be on par with the Mac. There is NSFileWrapper, but NSTextAttachment (which is used for images on the Mac) didn’t make it into iOS so far.
The next big chance for Apple to completely sherlock DTCoreText+DTRichTextEditor+DTLoupeView+DTWebArchive is in Summer 2013 with iOS 7. This leaves these components a useful lifetime of at least a year from now.
The next big thing to do with DTCoreText is to add support for the new NS* tags where they can replace the CoreFoundation-based previous ones. Like for example NSParagraphStyle can replace CTParagraphStyle which is no Obj-C object and such had forced me to create the DTParagraphStyle wrapper in DTCoreText. I envision to detect the OS version this is running on and then use the new attributes where available or fall back on the old ones where not.
An attributed string that only uses attributes that are NSCoding-compliant (i.e. not CoreFoundation non-bridgable, but actual Objective-C objects) would also gain the ability of being persisted with NSKeyedArchiver. This would also solve a problem many people are experiencing. The only current way I have to persist attributed strings is by creating a so-so HTML representation. NSCoding would also allow for much faster caching of attributed strings because you could persist anywhere that accepts NSData, like for example a CoreData table.
As far as I am aware NSAttributedStrings are NSCoding-compliant on the Mac. So it would be awesome if the outcome would be platform-independent to allow transferring thus persisted attributed strings between iOS and Mac.
All those big plans however have a big problem: so far I only found 2 companies willing to pay for enhancements on DTCoreText. I need to make a living as well and without somebody footing the bill I can only spend a few minutes here and there on it. DTCoreText is done entirely for the benefit of the community as I have no apps myself using it. So if you are interested in sponsoring improvements please get in touch.