The text view classes UITextView, UITextField and UILabel (and maybe a few others) have been extended to handle attributed strings. Great, right?
Well, not really.
First, if you want to support iOS 5 also, you're out of luck.
Next, the iOS version of NSAttributedString doesn't have any way to create an attributed string from an RTF file.
The Mac OS version of NSAttributedString has a handy method, initWithRTF:documentAttributes: that lets you create an attributed string from an RTF file. So you an include an RTF file in your project, edit in Xcode (who's editor knows how to edit styled rtf documents, cool!) and then write a couple of lines of code that load the contents of that rtf file into an NSAttributedString and display it.
Not so on iOS. There is no easy way to create NSAttributedString objects.
Ok, so I looked into it, and NSAttributedString supports NSCoding.
I wrote a little Mac tool that takes an RTF file and saves it out as a new file type, .data, which is just an NSArchiver output from sending the keyed archive class method archiveRootObject:toFile: an attributed string. Ok, so far so good.
Now I want to display a plain text version of this string when running on iOS 5. iOS 5 supports attributed strings, but not text views that know how to display them.
So at runtime I write code that loads my .data file into an attributed string. I then check to see if my UITextField responds to the setAttributedText method. If it does, great. I set the attributedText property, and there you go. Under iOS 5, I simply use the NSAttributedString's string method to get a plain text string, and assign that to the text property of my text view. Simple, right?
Wrong. It turns out that the iOS 5 version of NSAttributedString doesn't know how to handle paragraph styles and a few other things that you take for granted when composing even a simple RTF document.
Grr. So now I have to go back to my Mac RTF to data conversion utility and make it export both a data archive of the object and a plain text version. My iOS app needs to decide at runtime which OS it's running on, and load the plain text under iOS 5 and the styled text .data file for iOS 6.
Now I have 3 versions of my styled text to deal with: the original RTF file, the .data iOS readable version, and the plain text version for iOS 5.
We're planning on localizing the app we are shipping for several languages, and so we will have a number of styled text files to deal with, and probably multiple revisions of those files. Sounds like a configuration management nightmare in the making.
I spent all day today devising a solution to this problem.
Here's what I did.
I turned my Mac utility that converts RTF files into data files+text files into a command line tool. It takes a source filename as a required parameter, and an optional output directory as a second parameter. (There's also an overwrite switch in case the output files already exist) If no output directory is specified, it tries to write the .data and .txt output files in the same directory as the source .rtf file.
I then spent the day figuring out how to integrate this tool into the Xcode build process for our app. It was NOT easy.
I got some help from stack overflow, and did a lot of fiddling.
The good news is that I now have full integration of rtf files with my project. If I add .rtf files to my project, the build process automatically generates the required .data and .txt files that I need to display that text under both iOS 5 and iOS 6. Since it's a build rule, it understands dependencies, and the conversion is only run if the source file changes. If I edit the RTF file in Xcode or TextEdit, the next time I run the app, the new content is automatically generated and installed in the app.
The bad news is what I had to do to get Xcode to run my command line tool as part of the build. The only way I could get it to work was to copy the command line binary inside the Xcode project bundle. (in /contents/Developer/usr/bin. Installing the tool in either the user level or system level bin directory doesn't work. WTF?
This solution works, but it's fragile. Any time I update Xcode, it's going to stop working and I will need to copy the file back into the new version of Xcode. Ugh. Ugh ugh ugh.
Does anybody out there know of a way that I can install the command line tool (not a script file, mind you, but a compiled binary tool) into the PROJECT directory, or as a second choice, as system directory? Modifying Xcode itself is a rather nasty, hackish solution.
Well, not really.
First, if you want to support iOS 5 also, you're out of luck.
Next, the iOS version of NSAttributedString doesn't have any way to create an attributed string from an RTF file.
The Mac OS version of NSAttributedString has a handy method, initWithRTF:documentAttributes: that lets you create an attributed string from an RTF file. So you an include an RTF file in your project, edit in Xcode (who's editor knows how to edit styled rtf documents, cool!) and then write a couple of lines of code that load the contents of that rtf file into an NSAttributedString and display it.
Not so on iOS. There is no easy way to create NSAttributedString objects.
Ok, so I looked into it, and NSAttributedString supports NSCoding.
I wrote a little Mac tool that takes an RTF file and saves it out as a new file type, .data, which is just an NSArchiver output from sending the keyed archive class method archiveRootObject:toFile: an attributed string. Ok, so far so good.
Now I want to display a plain text version of this string when running on iOS 5. iOS 5 supports attributed strings, but not text views that know how to display them.
So at runtime I write code that loads my .data file into an attributed string. I then check to see if my UITextField responds to the setAttributedText method. If it does, great. I set the attributedText property, and there you go. Under iOS 5, I simply use the NSAttributedString's string method to get a plain text string, and assign that to the text property of my text view. Simple, right?
Wrong. It turns out that the iOS 5 version of NSAttributedString doesn't know how to handle paragraph styles and a few other things that you take for granted when composing even a simple RTF document.
Grr. So now I have to go back to my Mac RTF to data conversion utility and make it export both a data archive of the object and a plain text version. My iOS app needs to decide at runtime which OS it's running on, and load the plain text under iOS 5 and the styled text .data file for iOS 6.
Now I have 3 versions of my styled text to deal with: the original RTF file, the .data iOS readable version, and the plain text version for iOS 5.
We're planning on localizing the app we are shipping for several languages, and so we will have a number of styled text files to deal with, and probably multiple revisions of those files. Sounds like a configuration management nightmare in the making.
I spent all day today devising a solution to this problem.
Here's what I did.
I turned my Mac utility that converts RTF files into data files+text files into a command line tool. It takes a source filename as a required parameter, and an optional output directory as a second parameter. (There's also an overwrite switch in case the output files already exist) If no output directory is specified, it tries to write the .data and .txt output files in the same directory as the source .rtf file.
I then spent the day figuring out how to integrate this tool into the Xcode build process for our app. It was NOT easy.
I got some help from stack overflow, and did a lot of fiddling.
The good news is that I now have full integration of rtf files with my project. If I add .rtf files to my project, the build process automatically generates the required .data and .txt files that I need to display that text under both iOS 5 and iOS 6. Since it's a build rule, it understands dependencies, and the conversion is only run if the source file changes. If I edit the RTF file in Xcode or TextEdit, the next time I run the app, the new content is automatically generated and installed in the app.
The bad news is what I had to do to get Xcode to run my command line tool as part of the build. The only way I could get it to work was to copy the command line binary inside the Xcode project bundle. (in /contents/Developer/usr/bin. Installing the tool in either the user level or system level bin directory doesn't work. WTF?
This solution works, but it's fragile. Any time I update Xcode, it's going to stop working and I will need to copy the file back into the new version of Xcode. Ugh. Ugh ugh ugh.
Does anybody out there know of a way that I can install the command line tool (not a script file, mind you, but a compiled binary tool) into the PROJECT directory, or as a second choice, as system directory? Modifying Xcode itself is a rather nasty, hackish solution.
Last edited: