33

I am developing an iPhone app, and I want to set kerning in UILabel. The code I've written (possibly around kCTKernAttributeName) seems to be in error. How might I approach fixing this?

NSMutableAttributedString *attStr;   
NSString *str = @"aaaaaaa";    
CFStringRef kern = kCTKernAttributeName;        
NSNumber *num = [NSNumber numberWithFloat: 2.0f];    
NSDictionary *attributesDict = [NSDictionary dictionaryWithObject:num 
forKey:(NSString*)kern];        
[attStr initWithString:str attributes:attributesDict];      
CGRect frame1 = CGRectMake(0, 0, 100, 40);    
UILabel *label1 = [[UILabel alloc] initWithFrame:frame1];    
label1.text = attStr    
[self.view addSubview:label1];
SSteve
  • 10,550
  • 5
  • 46
  • 72
kz00011
  • 331
  • 1
  • 3
  • 3

9 Answers9

61

Old question, but you can do it now (easily).

NSMutableAttributedString *attributedString;
attributedString = [[NSMutableAttributedString alloc] initWithString:@"Please get wider"];
[attributedString addAttribute:NSKernAttributeName value:@5 range:NSMakeRange(10, 5)];
[self.label setAttributedText:attributedString];

enter image description here

For Nov 2013, Just to expand on this great answer, here's some totally typical code. Usually you'd set the font as well. Note in the comments the old-fashioned way using ordinary old .text. Hope it helps someone

NSString *yourText = @"whatever";

UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,0,0)];

// simple approach with no tracking...
// label.text = yourText;
// [label setFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:24]];

NSMutableAttributedString *attributedString;

attributedString = [[NSMutableAttributedString alloc] initWithString:yourText];

[attributedString addAttribute:NSKernAttributeName
                         value:[NSNumber numberWithFloat:2.0]
                         range:NSMakeRange(0, [yourText length])];

[attributedString addAttribute:NSFontAttributeName
                         value:[UIFont fontWithName:@"HelveticaNeue-Light" size:24]
                         range:NSMakeRange(0, [yourText length])];

label.attributedText = attributedString;

label.textColor = [UIColor blackColor];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;

[label sizeToFit];
DBD
  • 23,075
  • 12
  • 60
  • 84
  • 5
    This is not kerning. This is tracking. – Undistraction Jun 26 '13 at 14:45
  • 3
    Apple specifically uses 'Kern' to describe this letter spacing behavior. Is Apple's internal API really setting tracking instead of kerning? Maybe. Regardless, it seemed like this would provide a good solution for people looking for this type of text formatting. – DBD Jun 26 '13 at 15:34
  • 3
    Sure. But evenly increasing the space is letter-spacing or tracking. Kerning is letter-pair specific. Apple are using the wrong terminology. – Undistraction Jun 26 '13 at 17:09
  • 6
    Since attributed strings handle formatting on a character-by-character basis instead of a string basis, technically it is in fact kerning. – Rembrandt Q. Einstein Jul 15 '13 at 20:18
  • Please note this solution is only applicable in iOS 6+ – sixstatesaway Sep 24 '13 at 08:21
  • This code is in fact doing specific letter pairs -- note how you supply a range. It is setting them all to the same value (ie "5"). – Fattie Nov 28 '13 at 13:04
  • And furthermore, "ledding" is pronounced "ledding" (just as in "Led Zepplin.") It comes from the metallic substance, Pb, led. It drives me nuts when people pronounce that word the wrong way!! :) – Fattie Nov 28 '13 at 13:07
  • @Undistraction - they FINALLY added tracking (not kerning) for google ... **it's important to note 2023 that Apple now have actual TRACKING, not just KERNing** – Fattie Mar 17 '23 at 11:05
25

Before:

before

After:

cafter

Here's a Swift 3 extension that let's you set a UILabel's kerning via code or storyboard:

extension UILabel {

    @IBInspectable var kerning: Float {
        get {
            var range = NSMakeRange(0, (text ?? "").count)
            guard let kern = attributedText?.attribute(NSAttributedStringKey.kern, at: 0, effectiveRange: &range),
                let value = kern as? NSNumber
                else {
                    return 0
            }
            return value.floatValue
        }
        set {
            var attText:NSMutableAttributedString

            if let attributedText = attributedText {
                attText = NSMutableAttributedString(attributedString: attributedText)
            } else if let text = text {
                attText = NSMutableAttributedString(string: text)
            } else {
                attText = NSMutableAttributedString(string: "")
            }

            let range = NSMakeRange(0, attText.length)
            attText.addAttribute(NSAttributedStringKey.kern, value: NSNumber(value: newValue), range: range)
            self.attributedText = attText
        }
    }
}

Demo usage:

myLabel.kerning = 3.0

or

enter image description here

The demo uses 3.0 kerning for drama, but I've found 0.1 - 0.8 tends to work well in practice.

vikzilla
  • 3,998
  • 6
  • 36
  • 57
Andrew Schreiber
  • 14,344
  • 6
  • 46
  • 53
  • Perfect! That's the only extension that works without any warning for me. Do you know some extension that'll do the same for LineHeight? – suicidebilly Mar 24 '16 at 20:39
  • 2
    If you set the kerning in a storyboard, and then change the label text in code, the kerning is not applied to the new text. You can re-apply the kerning in code and that will make it work, or you could perhaps subclass UILabel and override setText to re-apply kerning if it's set. – Josh Vickery Jun 29 '16 at 15:24
  • NICE! Your a champ ;) – Chris Mar 18 '20 at 17:01
19

Taking DBD's answer, I made a category on UILabel which allows setting the kerning if running on iOS6+ with graceful fall back to just setting text on previous iOS versions. Might be of help to others...

UILabel+TextKerning.h

#import <UIKit/UIKit.h>

@interface UILabel (TextKerning)

/**
 * Set the label's text to the given string, using the given kerning value if able.
 * (i.e., if running on iOS 6.0+). The kerning value specifies the number of points
 * by which to adjust spacing between characters (positive values increase spacing,
 * negative values decrease spacing, a value of 0 is default)
 **/
- (void) setText:(NSString *)text withKerning:(CGFloat)kerning;

/**
 * Set the kerning value of the currently-set text.  The kerning value specifies the number of points
 * by which to adjust spacing between characters (positive values increase spacing,
 * negative values decrease spacing, a value of 0 is default)
 **/
- (void) setKerning:(CGFloat)kerning;

@end

UILabel+TextKerning.m

#import "UILabel+TextKerning.h"

@implementation UILabel (TextKerning)

-(void) setText:(NSString *)text withKerning:(CGFloat)kerning
{
    if ([self respondsToSelector:@selector(setAttributedText:)])
    {
        NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];
        [attributedString addAttribute:NSKernAttributeName
                                 value:[NSNumber numberWithFloat:kerning]
                                 range:NSMakeRange(0, [text length])];
        [self setAttributedText:attributedString];
    }
    else
        [self setText:text];
}

-(void) setKerning:(CGFloat)kerning
{
    [self setText:self.text withKerning:kerning];
}
Jeff Hay
  • 2,655
  • 28
  • 32
6

Just to be up-to-date here, iOS 6 introduced attributedText for UILabel and UITextView!

UILabel reference:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UILabel_Class/Reference/UILabel.html#//apple_ref/occ/instp/UILabel/attributedText

calimarkus
  • 9,955
  • 2
  • 28
  • 48
5

Just do this in Swift:

    let myTitle = "my title"
    let titleLabel = UILabel()
    let attributes: NSDictionary = [
        NSFontAttributeName:UIFont(name: "HelveticaNeue-Light", size: 20),
        NSForegroundColorAttributeName:UIColor.whiteColor(),
        NSKernAttributeName:CGFloat(2.0)
    ]
    let attributedTitle = NSAttributedString(string: myTitle, attributes: attributes as? [String : AnyObject])

    titleLabel.attributedText = attributedTitle
    titleLabel.sizeToFit()
CodeOverRide
  • 4,431
  • 43
  • 36
3

An example using IBDesignables and IBInspectables where you can manage to set the kerning value through storyboard only. I found it very practical and I thought to share it with you.

UILabelKerning.h

#import <UIKit/UIKit.h>

IB_DESIGNABLE

@interface UILabelKerning : UILabel
@property (assign, nonatomic) IBInspectable int kerning;
@end

UILabelKerning.m

#import "UILabelKerning.h"

@implementation UILabelKerning


-(void)awakeFromNib {

    [self setTheAttributes];
}

- (id)initWithCoder:(NSCoder*)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self)
    {
        // Initialization code
    }

    return self;
}
-(void)setTheAttributes{
    NSMutableAttributedString *attributedString =[[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
    [attributedString addAttribute:NSKernAttributeName
                             value:[NSNumber numberWithFloat:self.kerning]
                             range:NSMakeRange(0, [self.text length])];
    [self setAttributedText:attributedString];
}
@end

enter image description here

enter image description here

enter image description here

Arben Pnishi
  • 591
  • 5
  • 11
  • Attention please! I adopt this solution but unfortunately my app was rejected because "kerning" is a property name used in private api. – christian mini Sep 07 '16 at 08:05
  • @christianmini Sorry to hear that! What private api? – Arben Pnishi Sep 07 '16 at 08:48
  • this is the message:Performance - 2.5.1 Your app uses or references the following non-public APIs: kerning. The use of non-public APIs is not permitted on the App Store because it can lead to a poor user experience should these APIs change............So I think the problem is only in the name of the property – christian mini Sep 07 '16 at 08:58
  • If you are going to try with the property name changed and if it is accepted at App Store please let me know so I will change it here also :) – Arben Pnishi Sep 07 '16 at 09:18
  • @ Arben Pnishi Certainly – christian mini Sep 07 '16 at 09:48
1

Swift 4 and 5

extension NSAttributedString {

    /// Returns a new instance of NSAttributedString with same contents and attributes with kerning added.
    /// - Parameter kerning: a kerning you want to assign to the text.
    /// - Returns: a new instance of NSAttributedString with given kerning.
    func withKerning(_ kerning: CGFloat) -> NSAttributedString {
        let attributedString = NSMutableAttributedString(attributedString: self)
        attributedString.addAttributes([.kern: kerning],
                                       range: NSRange(location: 0, length: string.count))
        return NSAttributedString(attributedString: attributedString)
    }
 ]
Rashid Latif
  • 2,809
  • 22
  • 26
1

As far as I am aware, UILabel will not render the characteristics of NSAttributedString. There are a couple of nice open source solutions. I recently used TTTAttributedLabel as a swap in replacement for UILabel that accepts NSAttributedString.

DTCoreText (former NSAttributedString+HTML) is also getting a bit of buzz lately.

Volo
  • 28,673
  • 12
  • 97
  • 125
Eoin
  • 767
  • 3
  • 12
  • thanks comment. I knew Core Text String Attributes refer to kCTKernAttributeName instead of NSAttributedString. So, I checked this code. then I found the error sentence "[attStr initWithString:str attributes:attributesDict]". just a bit more, I would like to try to fix this code. – kz00011 Sep 10 '11 at 15:57
  • Make sure that you're linking to the CoreText framework as well as importing the CoreText header. In saying that, UILabel will not do what you're looking to do. Much easier to use on the replacements I mentioned. – Eoin Sep 10 '11 at 23:04
  • too late re-comment. I understood your comment. I am going to set by HTML.thank you very much! – kz00011 Sep 11 '11 at 13:52
  • Actually label has an **attributedText** property. – Borzh Oct 01 '17 at 22:34
-1

In Swift 2.0...

Add an extension:

extension UIView {
    func attributes(font: String, color: UIColor, fontSize: CGFloat, kern: Double) -> [String: NSObject] {
        let attribute = [
            NSForegroundColorAttributeName: color,
            NSKernAttributeName: kern,
            NSFontAttributeName : UIFont(name: font, size: fontSize)!
        ]
        return attribute
    }
}

Now, just set your UILabel as attributedText:

self.label.attributedText = NSMutableAttributedString(string: "SwiftExample", attributes: attributes("SourceSans-Regular", color: UIColor.whiteColor(), fontSize: 20, kern: 2.0))   

Obviously, I added a bunch of parameters that you may not need. Play around -- feel free to rewrite the method -- I was looking for this on a bunch of different answers so figured I'd post the whole extension in case it helps someone out there... -rab

rab_w
  • 19
  • 3