Fattie
Fattie

Reputation: 12332

Subclass UIButton with system font, bold, and kerning

You have to use attributed text to do kerning. But. On the other hand, you can't get at system fonts in the IB attributed text menu.

How do I make (in code) a UIButton which has

Ideally it would collect the text of the button from plain text Title in IB. But if the text has to be set in code that is fine.

class NiftyButton: UIButton { 
    ????
}

Normally I initialize UIButton like this .. but I don't even know if that's the best place to do this? (You can't do it in layoutSubviews, since it will loop of course.)

class InitializeyButton: UIButton {

    override init(frame: CGRect) {
        super.init(frame: frame)
        common()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        common()
    }

    func common() {
        ...
    }
}

How to achieve in code ...

Upvotes: 0

Views: 711

Answers (2)

Jawad Ali
Jawad Ali

Reputation: 14417

Here is the class you can use to get the desired result:

class InitializeyButton: UIButton {

   @IBInspectable var spacing:CGFloat = 0 {
      didSet {
        updateTitleOfLabel()
      }
   }

   override func setTitle(_ title: String?, for state: UIControl.State) {

        let color = super.titleColor(for: state) ?? UIColor.black
        let attributedTitle = NSAttributedString(
            string: title ?? "",
            attributes: [NSAttributedString.Key.kern: spacing,
                         NSAttributedString.Key.foregroundColor: color,
                         NSAttributedString.Key.font: UIFont.systemFont(ofSize: self.titleLabel?.font.pointSize ?? 11, weight: .bold)   ])
        super.setAttributedTitle(attributedTitle, for: state)
   }

  private func updateTitleOfLabel() {
    let states:[UIControl.State] = [.normal, .highlighted, .selected, .disabled]
      for state in states {
        let currentText = super.title(for: state)
        self.setTitle(currentText, for: state)
      }
   }
}

Upvotes: 1

Fattie
Fattie

Reputation: 12332

Copy-paste UIButton for systemFont + kerning:

I've tidied up Jawad's excellent information:

Firstly, at bringup time, you have to create the attributed title:

import UIKit

class SmallChatButton: UIIButton { // (note the extra "I" !!)
    
    override func common() {
        super.common()
        backgroundColor = .your corporate color
        contentEdgeInsets = UIEdgeInsets(top: 7, left: 11, bottom: 7, right: 11)
        titles()
    }
    
    private func titles() {
        let states: [UIControl.State] = [.normal, .highlighted, .selected, .disabled]
        for state in states {
            let currentText = super.title(for: state)
            setTitle(currentText, for: state)
        }
    }
    

so to achieve that ..

    override func setTitle(_ title: String?, for state: UIControl.State) {

        let _f = UIFont.systemFont(ofSize: 10, weight: .heavy)
        
        let attributedTitle = NSAttributedString(
            string: title ?? "Click",
            attributes: [
                NSAttributedString.Key.kern: -0.5,
                NSAttributedString.Key.foregroundColor: UIColor.white,
                NSAttributedString.Key.font: _f
        ])
        setAttributedTitle(attributedTitle, for: state)
    }
    
    override func layoutSubviews() { // example of rounded corners
        super.layoutSubviews()
        layer.cornerRadius = bounds.height / 2.0
        clipsToBounds = true
    }
}

Note that a kern of "-0.5" is about what most typographers want for typical "slightly tight type".

It looks good with say all caps, or a slightly bold font, or small type. The Apple measurement system is unlike anything used by typographers, so, you'll just have to vary it until the typographer on your app is satisfied.

What is UIIButton (note the extra "I" !!)

Inevitably in any project you will need a UIButton which has an "I" initializer, so you'll proabbly have:

import UIKit

class UIIButton: UIButton {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        common()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        common()
    }
    
    func common() {
        
    }

}

(It's quite amazing that in iOS one has to do all of the above to "kern the system font" !)

Upvotes: 0

Related Questions