How to change letter spacing of UIButton in Swift?

Swift 4

extension UIButton{

    func addTextSpacing(_ letterSpacing: CGFloat){
        let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
        attributedString.addAttribute(NSAttributedString.Key.kern, value: letterSpacing, range: NSRange(location: 0, length: (self.titleLabel?.text!.count)!))
        self.setAttributedTitle(attributedString, for: .normal)
    }

}

// Usage: button.addTextSpacing(5.0)

Not a full answer, but a GOTCHA, and a FIX.

GOTCHA: applying character spacing also adds the space to the END of the text. This misfeature/bug means that center-aligned text will appear incorrectly.

FIX: when creating a Range for the attributed text, subtract 1 from the text.count value (thus ignoring the last character in the string for spacing purposes.)

e.g. incorrect centering due to extra space:

enter image description here

fixed:

enter image description here

[edited]

On a related note, if you are using EdgeInsets to impose padding around the text, your UIButton subclass will need to override intrinsicContentsSize:

override open var intrinsicContentSize: CGSize {
    let size = super.intrinsicContentSize
    let insets = self.titleEdgeInsets
    let width = size.width + insets.left + insets.right
    let height = size.height + insets.top + insets.bottom
    return CGSize(width: width, height: height)
}

  1. Make the NSAttributedString like in the question you linked
  2. Call setAttributedTitle(_ ,forState:) on your UIButton

Try this (untested):

let title = agreementButton.titleForState(.Normal)
let attributedTitle = NSAttributedString(string: title, attributes: [NSKernAttributeName: 1.0])
agreementButton.setAttributedTitle(attributedTitle, forState: .Normal)

Swift 3.0

extension UIButton{
   func addTextSpacing(spacing: CGFloat){
       let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
       attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: (self.titleLabel?.text!.characters.count)!))
       self.setAttributedTitle(attributedString, for: .normal)
   }
}
btnRegister.addTextSpacing(spacing: 4.5)

extension UILabel{
    func addTextSpacing(spacing: CGFloat){
        let attributedString = NSMutableAttributedString(string: self.text!)
        attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: self.text!.characters.count))
        self.attributedText = attributedString
    }
}
lblOne.addTextSpacing(spacing: 4.5)

extension UITextField{
    func addPlaceholderSpacing(spacing: CGFloat){
        let attributedString = NSMutableAttributedString(string: self.placeholder!)
        attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: self.placeholder!.characters.count))
        self.attributedPlaceholder = attributedString
    }
}
txtUserName.addPlaceholderSpacing(spacing: 4.5)

extension UINavigationItem{
    func addSpacing(spacing: CGFloat){
        let attributedString = NSMutableAttributedString(string: self.title!)
        attributedString.addAttribute(NSKernAttributeName, value: spacing, range: NSRange(location: 0, length: self.title!.characters.count))
        let label = UILabel()
        label.textColor = UIColor.black
        label.font = UIFont.systemFont(ofSize: 15, weight: UIFontWeightBold)
        label.attributedText = attributedString
        label.sizeToFit()
        self.titleView = label
    }
}
navigationItem.addSpacing(spacing: 2.5)