开发者

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

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开发者_如何学JAVA'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!


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 );
}


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.


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

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

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

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

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:

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

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)
}


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 );
}


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.


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);
}


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!


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)


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)


You should set Font property earlier than Text property.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜