iosswiftnsattributedstringuifont

Change just Font of AttributedText in Swift


I have a number of UILabels created in IB which all have attributed text. Each label's text contains multiple lines of different font sizes and colors.

At run-time, I want to be able to change just the font name of these labels without changing the existing font sizes or colors.

I have researched and could not find a straight forward method to achieve this. Any ideas?


Solution

  • You first need to understand the lingo Apple uses to describe a typeface:

    What you want is to replace the font family of an attributed string.

    Swift 4

    // Enumerate through all the font ranges
    newAttributedString.enumerateAttribute(.font, in: NSMakeRange(0, newAttributedString.length), options: []) { value, range, stop in
        guard let currentFont = value as? UIFont else {
            return
        }
    
        // An NSFontDescriptor describes the attributes of a font: family name,
        // face name, point size, etc. Here we describe the replacement font as
        // coming from the "Hoefler Text" family
        let fontDescriptor = currentFont.fontDescriptor.addingAttributes([.family: "Hoefler Text"])
    
        // Ask the OS for an actual font that most closely matches the description above
        if let newFontDescriptor = fontDescriptor.matchingFontDescriptors(withMandatoryKeys: [.family]).first {
            let newFont = UIFont(descriptor: newFontDescriptor, size: currentFont.pointSize)
            newAttributedString.addAttributes([.font: newFont], range: range)
        }
    }
    
    label.attributedText = newAttributedString
    

    Swift 3

    let newAttributedString = NSMutableAttributedString(attributedString: label.attributedText)
    
    // Enumerate through all the font ranges
    newAttributedString.enumerateAttribute(NSFontAttributeName, in: NSMakeRange(0, newAttributedString.length), options: []) { value, range, stop in
        guard let currentFont = value as? UIFont else {
            return
        }
    
        // An NSFontDescriptor describes the attributes of a font: family name,
        // face name, point size, etc. Here we describe the replacement font as
        // coming from the "Hoefler Text" family
        let fontDescriptor = currentFont.fontDescriptor.addingAttributes([UIFontDescriptorFamilyAttribute: "Hoefler Text"])
    
        // Ask the OS for an actual font that most closely matches the description above
        if let newFontDescriptor = fontDescriptor.matchingFontDescriptors(withMandatoryKeys: [UIFontDescriptorFamilyAttribute]).first {
            let newFont = UIFont(descriptor: newFontDescriptor, size: currentFont.pointSize)
            newAttributedString.addAttributes([NSFontAttributeName: newFont], range: range)
        }
    }
    
    label.attributedText = newAttributedString
    

    Original (San Francisco):

    San Francisco

    Replacement (Hoefler Text):

    Hoefler Text