7

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?

5

3 Answers 3

11

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

  • Helvetica is a family
  • Helvetica Bold, Helvetica Italic, Helvetica Bold Italic, Helvetica Display etc. are faces
  • Helvetica Bold, 12pt is a font

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

Sign up to request clarification or add additional context in comments.

Comments

2

The above works great but with Swift4 and Xcode 9.1 I got number of warnings that method names had changed. Below is the result of applying all those warnings. Otherwise I did not change anything.

let newAttributedString = NSMutableAttributedString(attributedString: label.attributedText!)

// Enumerate through all the font ranges
newAttributedString.enumerateAttribute(NSAttributedStringKey.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([UIFontDescriptor.AttributeName.family: "Hoefler Text"])

    // Ask the OS for an actual font that most closely matches the description above
    if let newFontDescriptor = fontDescriptor.matchingFontDescriptors(withMandatoryKeys: [UIFontDescriptor.AttributeName.family]).first {
        let newFont = UIFont(descriptor: newFontDescriptor, size: currentFont.pointSize)
        newAttributedString.addAttributes([NSAttributedStringKey.font: newFont], range: range)
    }
}

label.attributedText = newAttributedString

Comments

2

More simple and short way:

    guard let atributedText = text else { return }
    let newAttributedString = NSMutableAttributedString(attributedString: atributedText)
    let newFont = UIFont.boldSystemFont(ofSize: fontSize.value)
    newAttributedString.addAttributes([.font: newFont], range: NSRange(0..<textLength))
    
    valueLabel.attributedText = newAttributedString

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.