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
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!
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)
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.
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
Below you can see that the y
of it subview is not the same of the UITextField
itself:
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:
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)
}
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);
}
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)