Using a custom font in a UITextField causes it to shift slightly when accessed -- is there a fix?
Asked Answered
P

10

24

I have a custom font in a UITextField, and I've noticed that when it's accessed (when the keyboard appears), the text shifts down by a very small amount -- maybe a pixel or two or three. (I've no way to measure it, of course, but it's enough for me to notice.) And then when the keyboard is dismissed, it shifts back up again.

I've tried clearing the field on editing (which hides the problem of the initial shift down), but it hasn't solved the problem. I've also looked over the various attributes in IB, and tried changing a few, but still the problem persists. I get the same results in the simulator and on my iPad2.

(The field is well clear of the keyboard, so it's not the entire view moving out of the way -- it's just the contents of that specific text field.)

I'm sure it's the custom font that's causing the problem -- it doesn't occur without it.

Any idea how to address this? I was thinking I might need to create the text field programmatically, instead of in IB -- and I know I probably ought to try that before asking the question here, but I'm loathe to go to all that trouble if it won't solve the problem.

Any advice appreciated!

Parfitt answered 14/6, 2011 at 23:24 Comment(5)
Just as a follow-up: creating the UITextField programmatically seems to solve the problem, at least in my informal testing. Go figure.Parfitt
I'm having the same problem, but instead of shifting, the font is changing back to Helvetica when editing. It changes back to the custom font when editing ends. This is on iOS 5 beta 3 by the way. Not sure if this helps in figuring out why this happens, but building all textFields programmatically would be a lot of extra work just for using a custom font.Knock
Bob, I'm having the same problem, the custom font changes when enter in editing mode. Did you resolve this problem? By the way, I'm using 4.3 version, so it seems this is not a beta bug. Thanks a lot.Konstantine
I gave the same problem and creating the text field's programmatically doesn't help (that's how I create it from the begging). Any more ideas how to solve this?Whirl
I solved this problem by fixing fonts, please check #7535998Daguerreotype
M
17

I had this issue as well.

To fix, subclass UITextField and implement the following methods to adjust the positioning of text when not editing and editing.

- (CGRect)textRectForBounds:(CGRect)bounds {

    return CGRectInset( bounds , 8 , 8 );
}

- (CGRect)editingRectForBounds:(CGRect)bounds {

    return CGRectInset( bounds , 8 , 5 );
}
Mortarboard answered 10/4, 2012 at 10:41 Comment(4)
This is the solution that worked for me. I was using a custom font which probably was the problem. By using this method and adjusting the values I fixed the issue.Submissive
dy unfortunately has no effect for me, instead i've created new bounds with shifted y. In ios7 this bug seem to be fixed.Nugget
The solution that you gave us is just a hack, it only works for one type of font and size, as soon as you change the font's size and/or the font's type the bug comes back. Thus it is not a solution which fixes all cases (combination of font type, font size and UITextField height), it's just a hack which temporary fixes the bug in one particular case. Other solutions to this problem would be either decompiling and modifying the font and/or changing the font's type and/or the font size's and/or the UITextField's height in interface builder.Jacindajacinta
@alino-91 But that's exactly the same solution that you've given elsewhere: https://mcmap.net/q/367562/-text-in-uitextfield-moves-up-after-editing-center-while-editingNormy
L
3

My solution is along the same lines a McDJ's, but with a slightly different twist. Subclass UITextField and override only these:

- (CGRect)placeholderRectForBounds:(CGRect)bounds {
  return CGRectOffset( [self editingRectForBounds:bounds], 0, 2 );
}

- (CGRect)editingRectForBounds:(CGRect)bounds {
  return CGRectOffset( [super editingRectForBounds:bounds], 0, -2 );
}

With the custom font I'm using, 2 points is the correct vertical adjustment, helping placeholder, "static", and "editing" text all stay on the same vertical line.

Leticia answered 9/9, 2012 at 21:50 Comment(2)
The solution that you gave us is just a hack, it only works for one type of font and size, as soon as you change the font's size and/or the font's type the bug comes back. Thus it is not a solution which fixes all cases (combination of font type, font size and UITextField height), it's just a hack which temporary fixes the bug in one particular case. Other solutions to this problem would be either decompiling and modifying the font and/or changing the font's type and/or the font size's and/or the UITextField's height in interface builder.Jacindajacinta
@alino-91 But that's exactly the same solution that you've given elsewhere: https://mcmap.net/q/367562/-text-in-uitextfield-moves-up-after-editing-center-while-editingNormy
K
2

Unfortunately none of the answers worked for me.

@blackjacx answer worked but only sometimes :(

I started out debugging and here is what I've discovered:

1 - The real problem seems to be with a private subview of UITextField of type UIFieldEditorContentView

UITextField view hieararchy

Below you can see that the y of it subview is not the same of the UITextField itself:

Y is not set to 0

After realizing it I came out with the following workaround:

override func layoutSubviews() {
    super.layoutSubviews()
    fixMisplacedEditorContentView()
}

func fixMisplacedEditorContentView() {
    if #available(iOS 10, *) {
        for view in subviews {
            if view.bounds.origin.y < 0 {
                view.bounds.origin = CGPoint(x: view.bounds.origin.x, y: 0)
            }
        }
    }
}

You will need to subclass UITextField and override layoutSubviews to add the ability to manually set to 0 the y of any subview that is set to a negative value. As this problem doesn't occur with iOS 9 our below I added a check to do the workaround only when it is on iOS 10.

The result you can see below:

Working properly

2 - This workaround doesn't work if the user choose to select a subrange of the text (selectAll works fine)

Since the selection of the text is not a must have for my app I rather disable it. In order to do that you can use the following code (Swift 3):

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    if #available(iOS 10, *) {
        if action == #selector(UIResponderStandardEditActions.select(_:)) {
            return false
        }
    }

    return super.canPerformAction(action, withSender: sender)
}
Kailyard answered 13/4, 2017 at 19:29 Comment(0)
A
1

Works for all font sizes and does not cause de-alignment with clearButton.

Subclass UITextField and override these as follows:

- (CGRect)placeholderRectForBounds:(CGRect)bounds
{
    return CGRectOffset( bounds, 0, 4 );
}

- (CGRect)editingRectForBounds:(CGRect)bounds
{
    return CGRectOffset( bounds, 0, 2);
}

- (CGRect)textRectForBounds:(CGRect)bounds
{
    return CGRectOffset( bounds , 0 , 4 );
}
Arcanum answered 13/11, 2012 at 15:24 Comment(2)
The solution that you gave us is just a hack, it only works for one type of font and size, as soon as you change the font's size and/or the font's type the bug comes back. Thus it is not a solution which fixes all cases (combination of font type, font size and UITextField height), it's just a hack which temporary fixes the bug in one particular case. Other solutions to this problem would be either decompiling and modifying the font and/or changing the font's type and/or the font size's and/or the UITextField's height in interface builder.Jacindajacinta
I was working for all then. I don't know how it is doing now.Arcanum
R
1

For a strange reason I didn't really understood I've solved this by setting automaticallyAdjustsScrollViewInsets to NO (or equivalent in Interface Builder). This with iOS 8.1.

Rogers answered 24/2, 2016 at 17:13 Comment(0)
C
0

I had this issue with a custom font and solved it by shifting the label in the other direction when the keyboard events would fire. I moved the center of the label in the button by overriding the drawRect: method

- (void)drawRect:(CGRect)rect
{
    self.titleLabel.center = CGPointMake(self.titleLabel.center.x, self.titleLabel.center.y+3);
}
Caron answered 3/2, 2012 at 3:27 Comment(0)
J
0

This is expected behaviour in a standard UITextField. You can however solve this by subclassing UITextField and by adjusting the bounds for the text itself.

Swift 3

override func textRect(forBounds bounds: CGRect) -> CGRect {
    return(bounds.insetBy(dx: 0, dy: 0))
}

override func editingRect(forBounds bounds: CGRect) -> CGRect {
    return(bounds.insetBy(dx: 0, dy: -0.5))
}

override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
    return(bounds.insetBy(dx: 0, dy: 0))
}

This should do the trick!

Jat answered 27/10, 2016 at 11:18 Comment(0)
U
0

Set your textfields Border Style to any value except "none" in IB, then, in your ViewController's viewDidLoad set:

yourTextField.borderStyle = .none

(Based on this answer by Box Jeon)

Unalterable answered 2/2, 2017 at 22:25 Comment(0)
H
0

Swift 3

Do not forget the accessory views of the UITextField. You'll need to account for super of the *rect(forBounds: ...) functions if you want a working implementation. And be also sure to only displace the rects for the buggy iOS 10 and not for 9 or 8! The following code should do the trick:

public class CustomTextField: UITextField {
    public override func textRect(forBounds bounds: CGRect) -> CGRect {
        let superValue = super.textRect(forBounds: bounds)

        if #available(iOS 10, *) {
            return superValue.insetBy(dx: 0, dy: 0)
        }
        return superValue
    }

    public override func editingRect(forBounds bounds: CGRect) -> CGRect {
        let superValue = super.editingRect(forBounds: bounds)

        if #available(iOS 10, *) {
            return superValue.insetBy(dx: 0, dy: -0.5)
        }
        return superValue
    }

    public override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
        let superValue = super.placeholderRect(forBounds: bounds)

        if #available(iOS 10, *) {
            if isEditing {
                return superValue.insetBy(dx: 0, dy: 0.5)
            }
            return superValue.insetBy(dx: 0, dy: 0.0)
        }
        return superValue
    }
}

EDIT

I slightly edited my code from above to the following and it works better for me. I testet it on iPhone 6, 6s, 7, 7s as well as the 'plus' devices with iOS 9.3 and 10.3.

public class CustomTextField: UITextField {

    public override func textRect(forBounds bounds: CGRect) -> CGRect {
        let superValue = super.textRect(forBounds: bounds)

        if #available(iOS 10, *) {
            return superValue.insetBy(dx: 0, dy: -0.3)
        }
        return superValue.insetBy(dx: 0, dy: -0.2)
    }

    public override func editingRect(forBounds bounds: CGRect) -> CGRect {
        return self.textRect(forBounds: bounds)
    }
}

I think it also depends on the font you use. I use UIFont.systemFont(ofSize: 17.0, weight: UIFontWeightLight)

Haight answered 5/4, 2017 at 23:44 Comment(2)
Unfortunately this seems to be a bug and this code won't do the trick at all times. It looks like it works for iPhone 7, but then I tried iPhone 7Plus and the half a pixel jump started happening again..Convection
But why this looks so ugly?! I even use the default font. Has anybody a suggestion?Haight
E
-1

You should set Font property earlier than Text property.

Ethelred answered 30/4, 2014 at 11:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.