iosswiftios8textkituifontdescriptor

swift alternative for fontDescriptorWithSymbolicTraits ios 8 bug


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:

  1. Select a text
  2. Click on "B" button
  3. Click on "I" button

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:

  1. Font Descriptor returns nil in iOS 8
  2. http://www.openradar.me/19922049

Solution

  • 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.