How do I change the letter spacing in a UILabel?
Asked Answered
A

10

22

I want to change the spacing between digits in a UIKit UILabel so that it is equal.

With standard spacing, the label looks like this:

label with uneven character spacing

I'd like it to look like this:

label with even character spacing

How can this be achieved?

Avalos answered 7/6, 2016 at 21:35 Comment(0)
P
26

You can use the NSKernAttributeName attribute on an attributed string:

UILabel *label = [UILabel new];

NSMutableAttributedString *text = [[NSMutableAttributedString alloc] 
                                   initWithString:@"127"];

// The value paramenter defines your spacing amount, and range is 
// the range of characters in your string the spacing will apply to. 
// Here we want it to apply to the whole string so we take it from 0 to text.length.
[text addAttribute:NSKernAttributeName 
             value:@-0.5 
             range:NSMakeRange(0, text.length)];

[label setAttributedText:text];
Photocopy answered 7/6, 2016 at 22:0 Comment(1)
Thanks J2K. I forgot about the ability to apply the kerning to a subrange of the string.Avalos
I
21

The easiest way is to create a custom UILabel class and set the letter spacing from Storyboard.

open class CustomLabel : UILabel {
    @IBInspectable open var characterSpacing:CGFloat = 1 {
        didSet {
            let attributedString = NSMutableAttributedString(string: self.text!)
            attributedString.addAttribute(NSKernAttributeName, value: self.characterSpacing, range: NSRange(location: 0, length: attributedString.length))
            self.attributedText = attributedString
        }

    }
}

enter image description here

Illustrious answered 24/4, 2017 at 7:12 Comment(2)
Default value will be never applied. You should fix the code.Cidevant
That's only the "easiest" way if you use interface builder.Avalos
V
10

On swift implementation looks like

let text = "TIPS, ARTICLES & BEST OFFERS"
label.attributedText = NSAttributedString(string: text, attributes: [.kern: 3.12])

NSAttributedString .kern attribute

Vellum answered 17/10, 2019 at 8:44 Comment(0)
D
6

You can use an NSAttributedString and play with the NSKernAttributeName attribute. The default value for this is 0 so you will want to set it to a negative number.

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSAttributedString_Class/#//apple_ref/doc/constant_group/Character_Attributes

in Obj-C you can do something like this:

NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc]initWithString: @"Test test test test "];
[attrStr addAttribute:NSKernAttributeName value:@(4.0) range:NSMakeRange(0, attrStr.length)];

label.attributedText = attrStr;

in Swift you could do something like this:

let myTitle = "my title"
let titleLabel = UILabel()
let attributes: NSDictionary = [
    NSFontAttributeName:UIFont(name: "FONT_NAME", size: TEXT_SIZE),
    NSKernAttributeName:CGFloat(2.0)
]

let attributedTitle = NSAttributedString(string: myTitle, attributes:attributes as? [String : AnyObject])

titleLabel.attributedText = attributedTitle
Droshky answered 7/6, 2016 at 22:5 Comment(0)
C
6

The NSKernAttributeName can be used.

But in correction to the other answers: Do not apply to the full text length, but (text.length - 1).

The negative or positive spacing is added to the letter and this is not required for the last one. Assume you would add a positive spacing it would end up in a spacing after the last letter. A centered string would not appear to be centered anymore. The same applies for negative spacing.

NSString *text = @"Sample Text";    
UILabel *label = [UILabel new]

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: text];
[attributedString addAttribute:NSKernAttributeName value:[NSNumber numberWithDouble:-1.0] range:NSMakeRange(0, text.length-1)];

[label setAttributedTitle:attributedString forState:UIControlStateNormal];

Applied to full text length.

Applied to full text length.

Applied to (text length - 1). Applied to (text length - 1).

Cuyp answered 4/7, 2017 at 12:5 Comment(1)
Very helpful addition! Just one thing: we had problems when passing in an empty string with the range being (0, -1). So I added max(0, text.length - 1) to make it safe.Fabrice
G
6

I've taken the answer from @J2K and translated it into a Swift 4.2 extension.

/// Set the text of the label but altering the kerning so that you can control the space between each characters.
///
/// - Parameters:
///   - text: New content of the label
///   - kerning: Set a value between 0 and 1 to lower the space between characters. Above 0, spacing will be increased. 0 disables kerning.
extension UILabel {

    func set(text: String, withKerning kerning: CGFloat) {
        let attributedString = NSMutableAttributedString(string: text)

        // The value parameter defines your spacing amount, and range is
        // the range of characters in your string the spacing will apply to.
        // Here we want it to apply to the whole string so we take it from 0 to text.count.
        attributedString.addAttribute(NSAttributedString.Key.kern, value: kerning, range: NSMakeRange(0, text.count))

        attributedText = attributedString
    }

}
Gloriagloriana answered 11/2, 2019 at 5:59 Comment(0)
C
3

If you want to apply the letter spacing attribute to all UILabels in Interface Builder without modifying anything (for particular font):

Swift 4:

/**
 * Applies letter spacing to selected fonts in UILabels from IB.
 *
 * - author Alexander Volkov
 * - version 1.0
 */
extension UILabel {

    /// Applies letter spacing
    open override func awakeFromNib() {
        super.awakeFromNib()
        applyLetterSpacing()
    }

    /// Applies letter spacing
    ///
    /// - Parameter aDecoder: the decoder
    /// - Returns: UILabel instance
    open override func awakeAfter(using aDecoder: NSCoder) -> Any? {
        let label = super.awakeAfter(using: aDecoder)
        self.applyLetterSpacing()
        return label
    }


    /// Applies letter spacing
    func applyLetterSpacing() {
        if font.fontName.contains("Oswald", caseSensitive: false) {
            let characterSpacing: CGFloat = 1
            let string = NSMutableAttributedString(string: self.text!)
            string.addAttribute(.kern, value: characterSpacing, range: NSRange(location: 0, length: string.length - 1))
            self.attributedText = string
        }
    }
}
Cidevant answered 12/3, 2018 at 3:1 Comment(0)
B
1
NSString *myString = @"127";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:myString];

float letterSpacing = -1.50f; // change spacing here
[attributedString addAttribute:NSKernAttributeName value:@(letterSpacing) range:NSMakeRange(0, [myString length])];
[myLabel setAttributedText:attributedString];

Also see this for more info and results: http://www.devsign.co/notes/tracking-and-character-spacing

Beaux answered 7/6, 2016 at 21:45 Comment(0)
H
0

I found this to be the best solution. It includes also UIButton, applies attributes that may have been set in interface builder and adapts to different fonts:

extension UILabel {

    open override func awakeFromNib() {
        super.awakeFromNib()
        applyLetterSpacing()
    }

    func applyLetterSpacing() {
        // return if empty text
        if((text ?? "").isEmpty) {
            return
        }

        // default spacing
        var spacing = 1
        if(font.familyName == "Source Serif Pro") {
            // custom spacing for different font
            spacing = 2
        }
        var attributes = attributedText?.attributes(at: 0, effectiveRange: nil) ?? [:]
        attributes[.kern] = spacing
        attributedText = NSAttributedString(string: text ?? "", attributes: attributes)
    }

}

and for Buttons

extension UIButton {

    open override func awakeFromNib() {
        super.awakeFromNib()
        applyLetterSpacing()
    }

    func applyLetterSpacing() {
        if((title(for: .normal) ?? "").isEmpty) {
            return
        }
        var attributes = attributedTitle(for: .normal)?.attributes(at: 0,     effectiveRange: nil) ?? [:]
        attributes[.kern] = 1
        attributes[.foregroundColor] = titleColor(for: .normal)
        setAttributedTitle(NSAttributedString(string: title(for: .normal) ?? "", attributes: attributes), for: .normal)
    }
}
Hydrotropism answered 20/4, 2020 at 12:13 Comment(0)
W
-1

click on the label and go to your attributes inspector. Change text from plain to attributed. You you will several options for spacing. Hope this helps.

Walton answered 7/6, 2016 at 21:54 Comment(1)
can you explain, because I see no option for the "letter spacing"Deeann

© 2022 - 2024 — McMap. All rights reserved.