Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 928
Kashif Avatar asked Feb 18 '17 17:02

Kashif


Video Answer


3 Answers

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

like image 85
Code Different Avatar answered Oct 20 '22 03:10

Code Different


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
like image 26
Barden Avatar answered Oct 20 '22 03:10

Barden


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
like image 1
ikakooo Avatar answered Oct 20 '22 03:10

ikakooo