There were 2 key parts to my question:
It turns out that iOS 7 added the ability to load attributed text from NSData
.
I created a custom subclass of UITextView
that takes advantage of the @IBInspectable
attribute and lets you load contents from an RTF file directly in IB. You simply type the filename into IB and the custom class does the rest.
Here are the details:
In iOS 7, NSAttributedString
gained the method initWithData:options:documentAttributes:error:
. That method lets you load an NSAttributedString from an NSData object. You can first load an RTF file into NSData, then use initWithData:options:documentAttributes:error:
to load that NSData into your text view. (Note that there is also a method initWithFileURL:options:documentAttributes:error:
that will load an attributed string directly from a file, but that method was deprecated in iOS 9. It's safer to use the method initWithData:options:documentAttributes:error:
, which wasn't deprecated.
I wanted a method that let me install clickable links into my text views without having to create any code specific to the links I was using.
The solution I came up with was to create a custom subclass of UITextView I call RTF_UITextView
and give it an @IBInspectable
property called RTF_Filename
. Adding the @IBInspectable
attribute to a property causes Interface Builder to expose that property in the "Attributes Inspector." You can then set that value from IB wihtout custom code.
I also added an @IBDesignable
attribute to my custom class. The @IBDesignable
attribute tells Xcode that it should install a running copy of your custom view class into Interface builder so you can see it in the graphical display of your view hierarchy. ()Unfortunately, for this class, the @IBDesignable
property seems to be flaky. It worked when I first added it, but then I deleted the plain text contents of my text view and the clickable links in my view went away and I have not been able to get them back.)
The code for my RTF_UITextView
is very simple. In addition to adding the @IBDesignable
attribute and an RTF_Filename
property with the @IBInspectable
attribute, I added a didSet()
method to the RTF_Filename
property. The didSet()
method gets called any time the value of the RTF_Filename
property changes. The code for the didSet()
method is quite simple:
@IBDesignable
class RTF_UITextView: UITextView
{
@IBInspectable
var RTF_Filename: String?
{
didSet(newValue)
{
//If the RTF_Filename is nil or the empty string, don't do anything
if ((RTF_Filename ?? "").isEmpty)
{
return
}
//Use optional binding to try to get an URL to the
//specified filename in the app bundle. If that succeeds, try to load
//NSData from the file.
if let fileURL = NSBundle.mainBundle().URLForResource(RTF_Filename, withExtension: "rtf"),
//If the fileURL loads, also try to load NSData from the URL.
let theData = NSData(contentsOfURL: fileURL)
{
var aString:NSAttributedString
do
{
//Try to load an NSAttributedString from the data
try
aString = NSAttributedString(data: theData,
options: [:],
documentAttributes: nil
)
//If it succeeds, install the attributed string into the field.
self.attributedText = aString;
}
catch
{
print("Nerp.");
}
}
}
}
}
Note that if the @IBDesignable property isn't going to reliably allow you to preview your styled text in Interface builder then it might be better to set the above code up as an extension of UITextView rather than a custom subclass. That way you could use it in any text view without having to change the text view to the custom class.
See my other answer if you need to support iOS versions prior to iOS 7.
You can download a sample project that includes this new class from gitHub:
DatesInSwift demo project on Github