How to set letter spacing of UITextField

20,864

Solution 1

No need to go for attributedText, which to be honest, was a mess implementing with modified spacing. As soon as I closed the keyboard the spacing disappeared, which prompted me to dig further.

Every UITextField has a property called defaultTextAttributes, which according to Apple "returns a dictionary of text attributes with default values.". The Apple document also says that "this property applies the specified attributes to the entire text of the text field"

Just find a suitable place in your code, usually where the textfield is being initialized and then copy and paste the following.

Answered in Swift 3.0

textfield.defaultTextAttributes.updateValue(spacing, forKey: NSKernAttributeName)

where spacing is of CGFloat type. For example 2.0

This works for different fonts as well.

Cheers!!


The latest syntax seems to be:

 yourField.defaultTextAttributes.updateValue(36.0, 
     forKey: NSAttributedString.Key.kern)

Solution 2

This is what eventually worked to set the kern for every change

    textField.addTarget(self, action: "textFieldDidChange", forControlEvents: .EditingChanged)

    func textFieldDidChange () {    
        let attributedString = NSMutableAttributedString(string: textField.text)
        attributedString.addAttribute(NSKernAttributeName, value: 5, range: NSMakeRange(0, count(textField.text)))
        attributedString.addAttribute(NSFontAttributeName, value: font, range: NSMakeRange(0, count(textField.text)))
        attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.blackColor(), range: NSMakeRange(0, count(textField.text)))

        textField.attributedText = attributedString
    }

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

        if count(textField.text) < 4 {
            return true
            // Else if 4 and delete is allowed
        }else if count(string) == 0 {
            return true
            // Else limit reached
        }else{
            return false
        }
    }

The problem however remains because different numbers have different widths, I will just resort back to making a UITextField for every digit.

Solution 3

Use the defaultTextAttributes property of UITextField. It will handle the conversion to NSAttributedString for you and apply the attributes you set. For example:

    NSMutableDictionary *attrs = [self.textField.defaultTextAttributes mutableCopy];
[attrs addEntriesFromDictionary:@{
    NSKernAttributeName: @18,
    NSUnderlineColorAttributeName: [UIColor grayColor],
    NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDash)
}];
self.textField.defaultTextAttributes = attrs;
Share:
20,864
KMV
Author by

KMV

Updated on July 20, 2020

Comments

  • KMV
    KMV almost 4 years

    I have an app in which the user has to type a four digit pin code. All digits have to be at a set distance from each other.

    Is there a way to do this if the PinTextField is a subclass of UIView? I know in a ViewController you could use UITextFieldTextDidChangeNotification and set the attributed text for each change. Notifications don't seem to work in a UIView though.

    Also I was wondering if there isn't a simpler way than making an attributed string for every update if you want to set the letter spacing of a UITextField text ?

    Correct spacing:

    TextField with correct spacing

    Wrong spacing:

    TextField with wrong spacing

  • KMV
    KMV almost 9 years
    shouldChangeCharactersInRange does not work for the first letter though, it only appears after you have input the second one.
  • Vishnuvardhan
    Vishnuvardhan almost 9 years
    check this link for first letter change, it may help you: stackoverflow.com/questions/7010547/…
  • Jurasic
    Jurasic over 6 years
    The proper solution is to use monospace font.
  • iOSer
    iOSer over 6 years
    @Fattie thank you for adding the latest syntax and for the comment as well. :)
  • Soumen
    Soumen almost 6 years
    If the TextField is secure then this solution is not working.
  • iOSer
    iOSer almost 6 years
    @Soumen sorry I have been busy with projects lately. But I will definitely look into this as soon as I get time. Thank btw for pointing that out
  • Ashu
    Ashu over 4 years
    How to remove space from last text. I have to only feed 4 digit. In that case it will wrong.
  • iOSer
    iOSer over 4 years
    @Ashu So you want three digits to be equally spaced and the last digit not so? Not sure what do you mean
  • Ashu
    Ashu over 4 years
    @iOSer yes, last digit should not have extra space
  • iOSer
    iOSer over 4 years
    Then Im afraid you'll have to use multiple texfields and take care of the spacing yourself.
  • rmp251
    rmp251 over 2 years
    Your entire second method can be nicely collapsed into one line: func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { string.isEmpty || count(textField.text) < 4 }