在界面构建器中更改UILabel的字符间距

70

有没有办法在Interface Builder上更改UILabel文本的字符间距(跟踪)?如果不行,是否有办法在已经创建带属性文本的UILabel上编程更改字符间距(跟踪)?


1
我99%确定Xcode 6不支持这个。 - Drux
3
@Drux 我99%确定Xcode 6可以使用IBDesignables。 - ninjaneer
14个回答

120

我知道这不是Interface Builder的解决方案,但您可以创建一个UILabel扩展程序,然后向任何您想要的UILabel添加间距:

extension UILabel {
  func addCharacterSpacing(kernValue: Double = 1.15) {
    guard let text = text, !text.isEmpty else { return }
    let string = NSMutableAttributedString(string: text)
    string.addAttribute(NSAttributedString.Key.kern, value: kernValue, range: NSRange(location: 0, length: string.length - 1))
    attributedText = string
  }
}

考虑将默认的kernValue1.15更改为适合您设计的值。
在设置文本值后,实施始终添加字符间距:
myLabel.text = "We used to be so close"
myLabel.addCharacterSpacing()

如果您计划在应用程序的不同位置使用不同的间距,您可以覆盖默认的字距值:
myLabelWithSpecialNeeds.addCharacterSpacing(kernValue: 1.3)

这个解决方案将覆盖您在UILabelattributedText上可能拥有的任何其他属性。


能否更改字符间距? - Priyal
@ Priyal,其中有一个1.15间距值...如果您需要更大的间距,请将其更改为更大的数字。 - budiDino
@budidino,从你的例子中我可以推断出你增加了单词之间的间距,但是我想改变字符间的间距,例如在We和user之间或者used和to之间...但是我想要控制We中W和e之间、user中u、s、e和r之间的间距。这是否可能? - Priyal
@budidino 谢谢!这是否相当于减小横向间距? - Priyal
2
非常好的回答!小建议:您可以将if语句更改为if let textString = text, textString.length > 0,以便在字符串为空时捕获索引越界 - inf1783
显示剩余2条评论

47

我现在先使用这个方法来获取现有的属性文本并进行修改,以添加字符间距:

let attributedString = discoveryTitle.attributedText as NSMutableAttributedString
attributedString.addAttribute(NSKernAttributeName, value: 1.0, range: NSMakeRange(0, attributedString.length))
discoveryTitle.attributedText = attributedString

Swift 3:

let attributedString = NSMutableAttributedString(string: discoveryTitle.text)
attributedString.addAttribute(NSKernAttributeName, value: CGFloat(1.0), range: NSRange(location: 0, length: attributedString.length))
discoveryTitle.attributedText = attributedString

在Swift 3中,使用NSRange代替NSMakeRange可以正常工作。


9
+1. 好极了的回答。如果你的文本必须居中,那么将字距应用于除最后一个字符外的所有字符,即将NSMakeRange(0, attributedString.length)更改为NSMakeRange(0, attributedString.length - 1)来源 - paulvs
@paulvs 是正确的。你还需要验证空字符串以避免崩溃。只需添加 max(0, attributedString.length - 1) 即可帮助解决问题。 - nahung89

25

对于完全静态的文本,比如视图的标题或特别是启动屏幕,您可以插入占用极少宽度(例如“l”字符)并具有0不透明度的字母。或者将其颜色设置为与背景相同。

我知道这并不是最美观的解决方案,但除了在Xcode中指定属性之前,这是唯一不需要编写任何代码并且有效的解决方案。

结果 如何

编辑/其他想法:要使间隔更加变化,您可以更改填充字符的字体大小。(感谢@mohamede1945提出的想法)


1
太棒了! :) 你甚至可以通过 '|' 字符的字体大小来控制宽度。你只需要创建一个并复制/粘贴即可 :) - mohamede1945
3
“@mohamede1945 好主意,在我实际阅读了你的评论并重新阅读了我的答案之前(这是相当久以前给出的),我也想到了可能改变字体大小的想法-然后看到了你的评论...如果两个人独立地提出同样的想法,那一定是好主意 ;)” - luk2302
5
嗨Luk - 我真的很爱你,但这个想法真的不好。对不起啊兄弟! - Fattie
2
考虑本地化,例如...它会搞砸它。 - Rasto
2
这对于可访问性来说是一个非常糟糕的想法。如果您使用此功能,请确保将可访问标签设置为实际文本。 - Sami Samhuri
显示剩余3条评论

22

Swift 3.2 和 Interface Builder

extension UILabel {

    @IBInspectable
    var letterSpace: CGFloat {
        set {
            let attributedString: NSMutableAttributedString!
            if let currentAttrString = attributedText {
                attributedString = NSMutableAttributedString(attributedString: currentAttrString)
            }
            else {
                attributedString = NSMutableAttributedString(string: text ?? "")
                text = nil
            }

            attributedString.addAttribute(NSKernAttributeName,
                                           value: newValue,
                                           range: NSRange(location: 0, length: attributedString.length))

            attributedText = attributedString
        }

        get {
            if let currentLetterSpace = attributedText?.attribute(NSKernAttributeName, at: 0, effectiveRange: .none) as? CGFloat {
                return currentLetterSpace
            }
            else {
                return 0
            }
        }
    }
}

在此输入图片描述


2
它完美地工作了。似乎我在我的代码中漏掉了一部分。对不起。 - Kunal Gupta
此内容不支持自定义字体。 - yaali
完美的方法。我还添加了相同的代码用于UILabel和UIButton,它们也可以工作。只需从titleLabel.text和titleLabel.attributedText获取文本即可适用于UIButton。 - Okhan Okbay
很棒的解决方案,只需要更新一些代码以适应Xcode的最新版本,但是Xcode会自动处理所有修复。谢谢! - Chris

13

Swift 5及更高版本

extension UILabel {
  func setTextSpacingBy(value: Double) {
    if let textString = self.text {
      let attributedString = NSMutableAttributedString(string: textString)
      attributedString.addAttribute(NSKernAttributeName, value: value, range: NSRange(location: 0, length: attributedString.length - 1))
      attributedText = attributedString
    }
  }
}

1
@craft 在此之前他的回答没有默认值,现在他已经进行了编辑。我也会进行编辑,谢谢。 - FredFlinstone

8

请尝试这个!!

创建自定义标签类

@interface CustomLabel : UILabel
@property (assign, nonatomic) CGFloat myLineSpacing;
@end


@implementation CustomLabel

- (void)setMyLineSpacing:(CGFloat)myLineSpacing {
    _myLineSpacing = myLineSpacing;
    self.text = self.text;
}

- (void)setText:(NSString *)text {
    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    paragraphStyle.lineSpacing = _myLineSpacing;
    paragraphStyle.alignment = self.textAlignment;
    NSDictionary *attributes = @{NSParagraphStyleAttributeName: paragraphStyle};
    NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text
                                                                         attributes:attributes];
    self.attributedText = attributedText;
}

并设置运行时属性

enter image description here

注意:这实际上是行距(也称为前导)。在很久以前(数字化之前),你会在行之间放置铅(金属)来增加行之间的间隔。对于字母间距,那被称为字距调整。这里是如何进行字距调整的https://dev59.com/yWEi5IYBdhLWcg3wltEl#21141156


1
漂亮 - 你不会在这里使用IBDesignables吗? - Fattie
1
但是这个问题是关于字母间距的,而不是行间距。 - uneven

7

为什么大家都在定义NSMUTABLEAttributedString。其实你不必显式地设置范围,这有时会使表情看起来很奇怪。 这是我的解决方案,在Swift 4中经过测试。

extension UILabel {
    func addCharactersSpacing(_ value: CGFloat = 1.15) {
        if let textString = text {
            let attrs: [NSAttributedStringKey : Any] = [.kern: value]
            attributedText = NSAttributedString(string: textString, attributes: attrs)
        }
    }
}

如果不指定范围,居中和右对齐的文本看起来会有点偏离标记,因为在最后一个字符之后会添加间距。虽然我可能完全错了 :) - budiDino

6

SWIFT 4 UILabel扩展:

import UIKit

extension UILabel {

    @IBInspectable
    var letterSpace: CGFloat {
        set {
            let attributedString: NSMutableAttributedString!
            if let currentAttrString = attributedText {
                attributedString = NSMutableAttributedString(attributedString: currentAttrString)
            } else {
                attributedString = NSMutableAttributedString(string: text ?? "")
                text = nil
            } 
            attributedString.addAttribute(NSAttributedString.Key.kern,
                                          value: newValue,
                                          range: NSRange(location: 0, length: attributedString.length))
            attributedText = attributedString
        }

        get {
            if let currentLetterSpace = attributedText?.attribute(NSAttributedString.Key.kern, at: 0, effectiveRange: .none) as? CGFloat {
                return currentLetterSpace
            } else {
                return 0
            }
        }
    }
}

3
这里有一个适用于Swift 4的解决方案,不会覆盖现有文本属性:
extension UILabel {

    /**
     Add kerning to a UILabel's existing `attributedText`
     - note: If `UILabel.attributedText` has not been set, the `UILabel.text`
     value will be returned from `attributedText` by default
     - note: This method must be called each time `UILabel.text` or
     `UILabel.attributedText` has been set
     - parameter kernValue: The value of the kerning to add
     */
    func addKern(_ kernValue: CGFloat) {
        guard let attributedText = attributedText,
            attributedText.string.count > 0,
            let fullRange = attributedText.string.range(of: attributedText.string) else {
                return
        }
        let updatedText = NSMutableAttributedString(attributedString: attributedText)
        updatedText.addAttributes([
            .kern: kernValue
            ], range: NSRange(fullRange, in: attributedText.string))
        self.attributedText = updatedText
    }
}

3
您可以使用下面的 Swift 4 UILabel 扩展程序,该扩展程序既考虑了现有的属性文本又考虑了纯文本,以便不覆盖现有的设置:
import UIKit

extension UILabel {
    func addCharacterSpacing(_ kernValue: Double = 1.30) {
        guard let attributedString: NSMutableAttributedString = {
            if let text = self.text, !text.isEmpty {
                return NSMutableAttributedString(string: text)
            } else if let attributedText = self.attributedText {
                return NSMutableAttributedString(attributedString: attributedText)
            }
            return nil
            }() else { return}

        attributedString.addAttribute(
            NSAttributedString.Key.kern,
            value: kernValue,
            range: NSRange(location: 0, length: attributedString.length)
        )
        self.attributedText = attributedString
    }
}

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