如何在iPhone的UILabel中设置字距(kerning)

33

我正在开发一款iPhone应用程序,我想在UILabel中设置字距(kerning)。我编写的代码(可能涉及kCTKernAttributeName)似乎存在错误。我应该如何解决这个问题?

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];

你能解释一下什么是字距调整吗? - Sandro Meier
1
字距(kerning)指的是字符之间的空格。 - kz00011
查看此答案以计算字距以适应标签的宽度:链接 - Borzh
重要的是要注意到2023年,苹果现在具有实际的跟踪功能,而不仅仅是字体调整。 - Fattie
9个回答

61

虽然这是一个老问题,但你现在可以很容易地完成它。

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

2013年11月,为了进一步扩展这个很好的答案,这里是一些完全典型的代码。通常您还会设置字体。请注意,在注释中使用普通的旧.text的老式方法。希望能对某人有所帮助。

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];

5
这不是字距调整(kerning),而是字间距调整(tracking)。 - Undistraction
3
苹果公司专门使用“Kern”来描述字母间距的行为。苹果公司内部的API是否真的将跟踪设置为字距调整?也许是的。不管怎样,这似乎为寻找此类文本格式的人们提供了一个很好的解决方案。 - DBD
3
可以。但是均匀增加间距被称为字间距或追踪(letter-spacing或tracking),而字距调整是针对特定字对的。苹果使用了错误的术语。 - Undistraction
6
由于属性字符串处理文本格式是基于每个字符而不是整个字符串,所以从技术上讲,这实际上是字距调整。 - Rembrandt Q. Einstein
请注意,此解决方案仅适用于iOS 6及以上版本。 - sixstatesaway
显示剩余3条评论

25

之前:

before

之后:

这里有一个 Swift 3 的扩展,可以通过 代码 或者 storyboard 来设置 UILabel 的字距:

cafter

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
        }
    }
}

演示用法:

myLabel.kerning = 3.0

或者

enter image description here

示例中使用了3.0的字距调整,但实际测试表明0.1-0.8效果更好。


太好了!这是唯一一个没有任何警告可以使用的扩展名。你知道有没有一些扩展可以对LineHeight执行相同的操作吗? - suicidebilly
1
天啊 - 你可以在扩展中使用@IBInspectable吗?太神奇了。但是它实际上对我不起作用...需要在哪里加上@IBDesignable吗? - Fattie
对于你们来说...当你们构建时它可以工作(太棒了,安德鲁!)- 但实际上并没有在Storyboard上显示更改? - Fattie
2
如果您在Storyboard中设置了字距(kerning),然后在代码中更改标签文本,则新文本不会应用字距。您可以在代码中重新应用字距,这样就可以使其正常工作,或者您可以子类化UILabel并覆盖setText以重新应用字距(如果已设置)。 - Josh Vickery
不错!你是冠军 ;) - Chris

19

根据DBD的回答,我在UILabel上创建了一个分类,它可以在运行于iOS6+上时设置字距,并优雅地回退到仅在以前的iOS版本上设置文本。这可能对其他人有所帮助...

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

->

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];
}

6

5

在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()

1
这是唯一一个不需要您事先知道字符串长度的答案。谢谢! - Şafak Gezer

3

一个使用IBDesignables和IBInspectables的示例,您可以通过故事板来管理设置字间距。

我觉得这非常实用,想与大家分享。

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


请注意!我采用了这个解决方案,但不幸的是我的应用程序被拒绝了,因为“kerning”是私有API中使用的属性名称。 - christian mini
@christianmini 很抱歉听到这个消息!是哪个私有API? - Arben Pnishi
这是信息:性能 - 2.5.1你的应用程序使用或引用以下非公开API:kerning。在App Store上不允许使用非公开API,因为如果这些API更改,可能会导致用户体验差。所以我认为问题只在属性名称中。 - christian mini
如果你打算更改属性名称并且它被App Store接受了,请告诉我,这样我也会在这里更改 :) - Arben Pnishi
@ Arben Pnishi 当然。 - christian mini

1
据我所知,UILabel不会呈现NSAttributedString的特性。有几个不错的开源解决方案。我最近使用了TTTAttributedLabel作为UILabel的替代品,它可以接受NSAttributedString。 DTCoreText(前NSAttributedString+HTML)最近也引起了一些关注。

谢谢您的评论。我知道Core Text字符串属性是指kCTKernAttributeName而不是NSAttributedString。于是我检查了这段代码, 然后我发现了错误句子"[attStr initWithString:str attributes:attributesDict]"。再加一点点功夫,我会尝试修复这段代码。 - kz00011
请确保您同时链接到CoreText框架并导入CoreText头文件。话虽如此,UILabel将无法完成您所要做的事情。使用我提到的替代方案会更容易些。 - Eoin
太晚了,我看到了你的评论。我将按照HTML设置。非常感谢! - kz00011
实际上 label 具有一个 attributedText 属性。 - Borzh

1

Swift 4和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)
    }
 ]

-1
在Swift 2.0中...
添加一个扩展:
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
    }
}

现在,只需将您的UILabel设置为attributedText:
self.label.attributedText = NSMutableAttributedString(string: "SwiftExample", attributes: attributes("SourceSans-Regular", color: UIColor.whiteColor(), fontSize: 20, kern: 2.0))   

显然,我添加了许多你可能不需要的参数。随意尝试一下--可以重写这个方法--我在许多不同的答案中寻找这个,所以想着把整个扩展都发布出来,以防对某人有所帮助...-rab


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接