I'm trying to create a Swift custom text editor (lite version) and got stuck on iOS 8 fontDescriptorWithSymbolicTraits bug, returning nil. Are there any Swift workarounds?
I've created a small project sample here , you can download and run the following scenario:
The app crashes, fontDescriptorWithSymbolicTraits
returns nil
:(
private func addOrRemoveTraitWithName(traitName: String, traitValue: UInt32) {
let range = self.textEditor.selectedRange
let currentAttributesDict = self.textEditor.textStorage.attributesAtIndex(range.location, effectiveRange: nil)
let currentFont = currentAttributesDict[NSFontAttributeName]
let fontDescriptor = currentFont?.fontDescriptor()
let fontNameAttribute = fontDescriptor?.fontAttributes()[UIFontDescriptorNameAttribute]
var changedFontDescriptor: UIFontDescriptor?
if fontNameAttribute?.rangeOfString(traitName).location == NSNotFound {
let existingTraitsWithNewTrait = UIFontDescriptorSymbolicTraits(rawValue: fontDescriptor!.symbolicTraits.rawValue | traitValue)
changedFontDescriptor = fontDescriptor?.fontDescriptorWithSymbolicTraits(existingTraitsWithNewTrait)
} else {
let existingTraitsWithoutTrait = UIFontDescriptorSymbolicTraits(rawValue: fontDescriptor!.symbolicTraits.rawValue & ~traitValue)
changedFontDescriptor = fontDescriptor?.fontDescriptorWithSymbolicTraits(existingTraitsWithoutTrait)
}
let updatedFont = UIFont(descriptor: changedFontDescriptor!, size: 0.0)
var dict = [String : AnyObject]()
dict[NSFontAttributeName] = updatedFont
self.textEditor.textStorage.beginEditing()
self.textEditor.textStorage.setAttributes(dict, range: range)
self.textEditor.textStorage.endEditing()
}
I highly recommend to take a quick look at sample project
What options do I have?
I also seen that on Apple documentation the method that was used by other is missing from the SDK & documentation
What others say about this:
You're right about this. The bug was fixed in iOS 8.3, fortunately. The only reliable workaround is to drop down to the level of Core Text and perform the trait application there. It's a pain, but it solves the problem.
Thus, for instance, before iOS 8.3, this doesn't work:
if let body = UIFont(name: "GillSans", size: 15),
emphasis = body.fontDescriptor().fontDescriptorWithSymbolicTraits(.TraitItalic) {
fbody = body
femphasis = UIFont(descriptor: emphasis, size: 0)
}
So you have to import CoreText
and drop down to that level:
if let body = UIFont(name: "GillSans", size: 15),
result = CTFontCreateCopyWithSymbolicTraits(body as CTFont, 0, nil, .ItalicTrait, .ItalicTrait) {
fbody = body
femphasis = result as UIFont
}
Fortunately, Swift 1.2 and later is aware that UIFont and CTFont are now toll-free bridged. Before Swift 1.2, this was even more complicated! And of course in earlier systems, they were not toll-free bridged and this was still more difficult.