Make part of a UILabel bold in Swift Make part of a UILabel bold in Swift ios ios

Make part of a UILabel bold in Swift


You will want to use attributedString which allows you to style parts of a string etc. This can be done like this by having two styles, one normal, one bold, and then attaching them together:

let boldText = "Filter:"let attrs = [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 15)]let attributedString = NSMutableAttributedString(string:boldText, attributes:attrs)let normalText = "Hi am normal"let normalString = NSMutableAttributedString(string:normalText)attributedString.append(normalString)

When you want to assign it to a label:

label.attributedText = attributedString


You can use NSMutableAttributedString and NSAttributedString to create customized string. The function below makes given boldString bold in given string.

Swift 3

func attributedText(withString string: String, boldString: String, font: UIFont) -> NSAttributedString {    let attributedString = NSMutableAttributedString(string: string,                                                     attributes: [NSFontAttributeName: font])    let boldFontAttribute: [String: Any] = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: font.pointSize)]    let range = (string as NSString).range(of: boldString)    attributedString.addAttributes(boldFontAttribute, range: range)    return attributedString}

Example usage

authorLabel.attributedText = attributedText(withString: String(format: "Author : %@", user.name), boldString: "Author", font: authorLabel.font)

Swift 4

func attributedText(withString string: String, boldString: String, font: UIFont) -> NSAttributedString {    let attributedString = NSMutableAttributedString(string: string,                                                     attributes: [NSAttributedStringKey.font: font])    let boldFontAttribute: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: font.pointSize)]    let range = (string as NSString).range(of: boldString)    attributedString.addAttributes(boldFontAttribute, range: range)    return attributedString}

Swift 4.2 and 5

func attributedText(withString string: String, boldString: String, font: UIFont) -> NSAttributedString {    let attributedString = NSMutableAttributedString(string: string,                                                 attributes: [NSAttributedString.Key.font: font])    let boldFontAttribute: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: font.pointSize)]    let range = (string as NSString).range(of: boldString)    attributedString.addAttributes(boldFontAttribute, range: range)    return attributedString}


Result:

enter image description here

Swift 4.2 & 5.0:

First off we create a protocol that UILabel, UITextField and UITextView can adopt.

public protocol ChangableFont: AnyObject {    var rangedAttributes: [RangedAttributes] { get }    func getText() -> String?    func set(text: String?)    func getAttributedText() -> NSAttributedString?    func set(attributedText: NSAttributedString?)    func getFont() -> UIFont?    func changeFont(ofText text: String, with font: UIFont)    func changeFont(inRange range: NSRange, with font: UIFont)    func changeTextColor(ofText text: String, with color: UIColor)    func changeTextColor(inRange range: NSRange, with color: UIColor)    func resetFontChanges()}

We want to be able to add multiple changes to our text, therefore we create the rangedAttributes property. It's a custom struct that holds attributes and the range in which they are applied.

public struct RangedAttributes {    public let attributes: [NSAttributedString.Key: Any]    public let range: NSRange    public init(_ attributes: [NSAttributedString.Key: Any], inRange range: NSRange) {        self.attributes = attributes        self.range = range    }}

Another problem is that UILabel its font property is strong and UITextField its font property is weak/optional. To make them both work with our ChangableFont protocol we include the getFont() -> UIFont? method. This also counts for UITextView its text and attributedText properties. That's why we implement the getter and setter methods for them as well.

extension UILabel: ChangableFont {    public func getText() -> String? {        return text    }    public func set(text: String?) {        self.text = text    }    public func getAttributedText() -> NSAttributedString? {        return attributedText    }    public func set(attributedText: NSAttributedString?) {        self.attributedText = attributedText    }    public func getFont() -> UIFont? {        return font    }}extension UITextField: ChangableFont {    public func getText() -> String? {        return text    }    public func set(text: String?) {        self.text = text    }    public func getAttributedText() -> NSAttributedString? {        return attributedText    }    public func set(attributedText: NSAttributedString?) {        self.attributedText = attributedText    }    public func getFont() -> UIFont? {        return font    }}extension UITextView: ChangableFont {    public func getText() -> String? {        return text    }    public func set(text: String?) {        self.text = text    }    public func getAttributedText() -> NSAttributedString? {        return attributedText    }    public func set(attributedText: NSAttributedString?) {        self.attributedText = attributedText    }    public func getFont() -> UIFont? {        return font    }}

Now we can go ahead and create the default implementation for UILabel, UITextField and UITextView by extending our protocol.

public extension ChangableFont {    var rangedAttributes: [RangedAttributes] {        guard let attributedText = getAttributedText() else {            return []        }        var rangedAttributes: [RangedAttributes] = []        let fullRange = NSRange(            location: 0,            length: attributedText.string.count        )        attributedText.enumerateAttributes(            in: fullRange,            options: []        ) { (attributes, range, stop) in            guard range != fullRange, !attributes.isEmpty else { return }            rangedAttributes.append(RangedAttributes(attributes, inRange: range))        }        return rangedAttributes    }    func changeFont(ofText text: String, with font: UIFont) {        guard let range = (self.getAttributedText()?.string ?? self.getText())?.range(ofText: text) else { return }        changeFont(inRange: range, with: font)    }    func changeFont(inRange range: NSRange, with font: UIFont) {        add(attributes: [.font: font], inRange: range)    }    func changeTextColor(ofText text: String, with color: UIColor) {        guard let range = (self.getAttributedText()?.string ?? self.getText())?.range(ofText: text) else { return }        changeTextColor(inRange: range, with: color)    }    func changeTextColor(inRange range: NSRange, with color: UIColor) {        add(attributes: [.foregroundColor: color], inRange: range)    }    private func add(attributes: [NSAttributedString.Key: Any], inRange range: NSRange) {        guard !attributes.isEmpty else { return }        var rangedAttributes: [RangedAttributes] = self.rangedAttributes        var attributedString: NSMutableAttributedString        if let attributedText = getAttributedText() {            attributedString = NSMutableAttributedString(attributedString: attributedText)        } else if let text = getText() {            attributedString = NSMutableAttributedString(string: text)        } else {            return        }        rangedAttributes.append(RangedAttributes(attributes, inRange: range))        rangedAttributes.forEach { (rangedAttributes) in            attributedString.addAttributes(                rangedAttributes.attributes,                range: rangedAttributes.range            )        }        set(attributedText: attributedString)    }    func resetFontChanges() {        guard let text = getText() else { return }        set(attributedText: NSMutableAttributedString(string: text))    }}

With in the default implementation I use a little helper method for getting the NSRange of a substring.

public extension String {    func range(ofText text: String) -> NSRange {        let fullText = self        let range = (fullText as NSString).range(of: text)        return range    }}

We're done! You can now change parts of the text its font and text color.

titleLabel.text = "Welcome"titleLabel.font = UIFont.systemFont(ofSize: 70, weight: .bold)titleLabel.textColor = UIColor.blacktitleLabel.changeFont(ofText: "lc", with: UIFont.systemFont(ofSize: 60, weight: .light))titleLabel.changeTextColor(ofText: "el", with: UIColor.blue)titleLabel.changeTextColor(ofText: "co", with: UIColor.red)titleLabel.changeTextColor(ofText: "m", with: UIColor.green)